Error: "NoSuchMethodError: 'length' method not found. Receiver: null" when updating map values
List<ImageDetails> _images = [
ImageDetails(
imagePath: 'assets/images/meal1.jpg',
date: '2021-11-30',
details:
'',
),
ImageDetails(
imagePath: 'assets/images/meal2.jpg',
date: '2021-11-30',
details:
'',
),
];
var dateToImages = new Map();
_images.sort((a,b) => a.date.compareTo(b.date));
//group images by date
for (int i = 0; i < _images.length; i++) {
var d = _images[i].date; //convert string to Datetime
print("printing datetime in for loop");
print(d);
if (dateToImages.containsKey(d)) {
print("second element");
var list = dateToImages[d].add(_images[i]);
dateToImages[d] = list;
} else {
print("first element");
dateToImages[d] = [_images[i]];
}
}
var sortedKeys = dateToImages.keys.toList()..sort((a, b) => a.compareTo(b));
List<Widget> children = [];
print("=====printing datetoImages");
print(dateToImages);
print("======== printing sortedKeys");
print(sortedKeys);
int len = dateToImages['2021-11-30'].length;
Below is result of running above code
printing datetime in for loop
2021-11-30
first element
printing datetime in for loop
2021-11-30
second element
=====printing datetoImages
{2021-11-30: null}
======== printing sortedKeys
[2021-11-30]
After printing some variables, it seems like the issue is with the value for key "2021-11-30" in dateToImages being null... I don't understand why I keep getting null since it seems like the map building process in the for loop seems to be going well? Can anyone shed some light on this bug?
Thanks!
The error message suggests you are accessing length method which is not available.
since null doesn't have length method but list have it.
So, you may have a logical error, where you think you are returning list but the code is returning null
In your case:
Your Code is fine except for this part:
if (dateToImages.containsKey(d)) {
print("second element");
var list = dateToImages[d].add(_images[i]);
dateToImages[d] = list;
}
add() method returns void which will return null to var list and null will be sent to the map
Update it directly, replace above code with this:
if (dateToImages.containsKey(d)) {
print("second element");
dateToImages[d].add(_images[i]);
}
try this :
List<ImageDetails> _images = [
ImageDetails(
imagePath: 'assets/images/meal1.jpg',
date: '2021-12-30',
details:
'',
),
ImageDetails(
imagePath: 'assets/images/meal2.jpg',
date: '2021-11-30',
details:
'',
),
];
var dateToImages = new Map();
_images.forEach((img){
if(dateToImages.containsKey(img.date)){
dateToImages[img.date].add(img);
}else{
dateToImages[img.date] = [img];
}
});
var sortedKeys = dateToImages.keys.toList()..sort((a, b) =>
a.compareTo(b));
print("=====printing datetoImages");
print(dateToImages);
print("======== printing sortedKeys");
print(sortedKeys);
int len = dateToImages['2021-11-30'].length;
Output:
=====printing datetoImages
{2021-12-30: [Instance of 'ImageDetails'], 2021-11-30: [Instance of
'ImageDetails']}
======== printing sortedKeys
[2021-11-30, 2021-12-30]
Related
How can I use firstWhereOrNull with maps in Flutter?
In other words, how can I do this:
final myVariable1 = myList.firstWhereOrNull(
(myVariable2) =>
!myList.containsValue(myVariable2));
Instead of using a list (myList), I'd like to do the same with a map (Map<String,int>).
Map<String,int> myMap = {};
myMap("stuff1") = 1;
myMap("stuff2") = 2;
myMap("stuff3") = 3;
Thanks
There is no such firstWhereOrNull method for Maps, but you can easily come up with one using extension methods:
extension ExtendedMap on Map {
/// The first entry satisfying test, or null if there are none.
MapEntry? firstWhereOrNull(bool Function(MapEntry entry) test) {
for (var entry in this.entries) {
if (test(entry)) return entry;
}
return null;
}
}
Here is how you can use it:
final map = <String, int>{
'stuff1': 1,
'stuff2': 2,
'stuff3': 3,
};
final test = map.firstWhereOrNull((entry) => entry.value == 2);
print(test); // Prints MapEntry(stuff2: 2)
final nullTest = map.firstWhereOrNull((entry) => entry.key == "stuff5");
print(nullTest); // Prints null
So, I created this implementation, I don't think it's the most optimized, also because it was necessary to use the cast since because of the sound null safety, it's not possible to return any value. But it works for you.
var myMap = {"key1": "value", "key2": 3};
var result = myMap.entries
.cast<dynamic>()
.firstWhere((e) => e.key == "key", orElse: () => null);
print(result);
I hope this helps in some way!
Example Code (Not working):
void main() {
List json = [
{
"sku": "SKU0001",
"uids": [
{
"uid": "U001"
}
]
}
];
var result = json.map( (item) {
item['no_desc'] = true; // able to add the key and bool value
item['uids'] = item['uids'].map( (uid) {
uid['is_scanned'] = true; // failed to add, but able to accept string value only.
return uid;
}).toList();
return item;
}).toList();
print(result);
}
return error 'bool' is not a subtype of type 'String'
Expected Result
[
{
sku: SKU0001,
no_desc: true,
uids: [
{
uid: U001,
is_scanned: true // must be boolean
}
]
}
]
It work when i try in String value.
uid['is_scanned'] = 'true';
How can I add bool value into the nested list?
If I am writing in javascript way,
it should be able to add the key into nested array ( list ).
But why in dart lang, it prompt me error?
Can someone expert in dart lang willing to explain to me?
Your issue can be fixed by creating a new Map instance in the inner .map method with the Map.from constructor.
item['uids'] = item['uids'].map( (uid) {
uid['is_scanned'] = true; // failed to add, but able to accept string value only.
return uid;
}).toList();
should be changed to:
item['uids'] = item['uids'].map( (uid) {
Map<String, dynamic> toReturn = Map.from(uid);
toReturn['is_scanned'] = true;
return toReturn;
}).toList();
I believe this issue is due to dart implicitly declaring the inner uids maps as Map<String, String> so creating a new instance changes this and allows you to assign any value to the keys of the Map.
Full working sample:
void main() {
List json = [
{
"sku": "SKU0001",
"uids": [
{
"uid": "U001"
}
]
}
];
var result = json.map( (item) {
item['no_desc'] = true;
item['uids'] = item['uids'].map( (uid) {
Map<String, dynamic> toReturn = Map.from(uid);
toReturn['is_scanned'] = true;
return toReturn;
}).toList();
return item;
}).toList();
print(result);
}
I am getting back information from a websocket that might contain one or more items on each response.
I want to convert these responses to a list of custom objects.
The issue I have is where I want to use the first index (key) and second index (value) of the returned data and use them to pass into the custom object initialization to instantiate them and add them to the list.
I also want to add to the list, but if the 'name' field in an object already exists then just update that objects amount value instead of adding another copy. I will just do this with a loop, unless there is a shorter way.
Below you can see some of the attempts I have tried.
EDIT:
var listAmounts = valueMap.values.toList();
var listNames = valueMap.keys.toList();
print(listAmounts[0]);
print(listNames[0]);
for (int i = 0; i < listAmounts.length; i++) {
Person newPerson =
new Person(name: listNames[i], amount: double.parse(listAmounts[i]));
coins.add(newPerson);
print('=========');
print(newPerson.name);
print(newPerson.amount.toString());
print('=========');
}
I am not sure this is a good way as would there be a possibility that the keys/values will not be in order? From testing they are but I am not 100% sure?
Here are two examples of the message received from the websocket:
example 1
{jacob: 123.456789, steven: 47.3476543}
example 2
{henry: 54.3768574}
I have a custom class:
class Person {
double amount;
String name;
Person({
this.name = '',
this.amount = 0.0,
});
}
Here is the main code:
IOWebSocketChannel channel;
List<Person> people = [];
void openChannel() async {
channel = IOWebSocketChannel.connect(
"wss://ws.example.com/example?example=exp1,exp2,exp3");
channel.stream.listen((message) {
//print(message);
Map valueMap = json.decode(message);
print(valueMap);
//people = List<Person>.from(valueMap.map((i) => Person.fromJson(i)));
//message.forEach((name, amount) => people.add(Person(name: name, amount: amount)));
});
}
var listAmounts = valueMap.values.toList();
var listNames = valueMap.keys.toList();
print(listAmounts[0]);
print(listNames[0]);
for (int i = 0; i < listAmounts.length; i++) {
Person newPerson =
new Person(name: listNames[i], amount: double.parse(listAmounts[i]));
coins.add(newPerson);
print('=========');
print(newPerson.name);
print(newPerson.amount.toString());
print('=========');
}
How can I initialize a list inside a map?
Map<String,Map<int,List<String>>> myMapList = Map();
I get an error :
The method '[]' was called on null.
I/flutter (16433): Receiver: null
I/flutter (16433): Tried calling: [](9)
By just adding the elements when you create the map, or later by adding them. For example:
var myMapList = <String, Map<int, List<String>>>{
'someKey': <int, List<String>>{
0: <String>['foo', 'bar'],
},
};
or
var m2 = <String, Map<int, List<String>>>{}; // empty map
m2['otherKey'] = <int, List<String>>{}; // add an empty map at the top level
m2['otherKey'][2] = <String>[]; // and an empty list to that map
m2['otherKey'][2].add('baz'); // and a value to that list
print(m2);
prints {otherKey: {2: [baz]}}
Try initializing using {}
Map<String,Map<int,List<String>>> myMapList = {}; // just add {}
it will create an empty map and works perfectly fine.
In case for list of int as value
void main() {
List<int> dividends = [99,101,176,182];
Map map = new Map<int, List<int>>();
dividends.forEach((i) {
if(i > 0) {
map[i] = <int>[];
for(int j=1; j <= i;j++) {
if(i%j == 0) {
map[i].add(j);
}
}
// print('$i, $map[$i]');
}
});
print(map);
}
IndexedTweets.find(searchParameters, function(err, indexedTweetsResults) {
var chunkSize, count, resultArray, size;
if (err != null) {
return console.log("Error!");
} else {
size = indexedTweetsResults.length;
count = 0;
chunkSize = 100;
resultArray = [];
indexedTweetsResults.forEach(function(tweet) {
console.log(tweet.user);
});
}
});
That's my code. My result looks like:
{ text: 'stuff',
user:
{ display_name: '...',
screen_name: '...'},
}
So why can't I get tweet.user? It just returns undefined.
If you are just getting back a string of JSON from mongo, you'll need to call JSON.parse(). If that's not the case then you should provide more code because it's not clear what the issue is.