Can someone explain me why is this happening, please!
Map<String, Object> pedidoUnitario = {
'Name': '',
};
List<Map<String, Object>> pedidoUser = [];
void main() {
pedidoUnitario.update('Name', (var val) => 'Lincoln');
pedidoUser.add(pedidoUnitario);
print(pedidoUser);
pedidoUnitario.update('Name', (var val) => 'Emilio');
pedidoUser.add(pedidoUnitario);
print(pedidoUser);
}
This is printing me
[{Name: Lincoln}]
[{Name: Emilio}, {Name: Emilio}]
I was hoping to see something like:
[{Name: Lincoln}, {Name: Emilio}]
Thanks in advance
You updated the same object. Then added it to the list. So now you have the same object in the list twice.
What you want is to create a new object and add that to the list.
This:
Map<String, Object> pedidoUnitario = {
'Name': '',
};
List<Map<String, Object>> pedidoUser = [];
void main() {
final firstEntry = Map<String, Object>.from(pedidoUnitario)
..update('Name', (var val) => 'Lincoln');
pedidoUser.add(firstEntry);
print(pedidoUser);
final secondEntry = Map<String, Object>.from(pedidoUnitario)
..update('Name', (var val) => 'Emilio');
pedidoUser.add(secondEntry);
print(pedidoUser);
}
will print
[{Name: Lincoln}]
[{Name: Lincoln}, {Name: Emilio}]
Related
I have a Map of List of Maps as a set of data (dataMap) and want to transform like below with using Map.update method. This caught an error: Uncaught Error: TypeError: Closure 'main__convertIdFromStringToDocRef_closure': type '(dynamic) => num' is not a subtype of type '(String) => String'. I should misunderstand something about Map.update, but am not sure what it is... Can you please teach me?
void main() {
num _convertStringToNum(dynamic str, String collection) {
if (str is String) { return num.tryParse(str); }
if (str is num) { return str; }
return null;
}
Map<String, dynamic> _convertIdFromStringToNum(Map<String, dynamic> map, String collection) {
map.update('id', (mapId) => _convertStringToNum(mapId, collection));
return map;
}
Map<String, dynamic> dataMap = {
'types': [
{
'id': '123',
'name': 'foo',
},
{
'id': '234',
'name': 'bar',
}
],
};
dataMap.update('types', (types) {
if (!(types is List<Map<String, dynamic>>)) { return null; }
types.map((Map<String, dynamic> type) => _convertIdFromStringToNum(type, 'types')).toList();
return types;
});
}
This code can run on DartPad.
I had strange issue as in Yours too.
Seems like if map entry is initialised with String it cannot be modified to num:
Map<String, dynamic> item = {
"id": "123",
"name": "TEST"
};
// this throws exception that `item['id']` has `String` value and we are trying to replace it with `num`
item['id'] = num.tryParse(item['id']);
I found workaround by creating custom class with constructor which converts input to necessary type of field:
class Type {
num id;
String name;
num convertToNum(dynamic input) {
switch (input.runtimeType) {
case int: break;
case num: break;
case String: return num.tryParse(input); break;
default: return null;
}
return input;
}
Type(id, name) {
this.id = convertToNum(id);
this.name = name;
}
static fromMap(Map map) {
return Type(map['id'], map['name']);
}
Map toMap() {
return {
"id": this.id,
"name": this.name
};
}
}
and while iterating list using .map I'm creating instance of Type and calling toMap method which returns unique object.
void main() {
Map<String, dynamic> dataMap = {
'types': [
{
'id': 111,
'name': 'foo',
},
{
'id': '123',
'name': 'foo',
},
{
'id': '234',
'name': 'bar',
}
],
};
dataMap['types'] = (dataMap['types'] is List<Map>)
? dataMap['types'].map((type) => Type.fromMap(type).toMap())
: null;
print(dataMap);
}
I found the solution by myself. (But still have an unclear points on the real cause...)
The problem is with .map(MapEntry<K2, V2> f(K key, V value)) method. The argument of the method is a function MapEntry<K2, V2> f(K key, V value), and as described in the code I expect Map<String, dynamic> on its key, but actually it is JsLinkedHashMap<String, String> (the value is String). As I tried assigning non-String value through map.update('id', (mapId) => _convertStringToNum(mapId, collection)); in the function _convertIdFromStringToNum, it throw the exception.
So I changed the code as below. I create a new Map<String, dynamic> (newMap) and then do .update() on it.
void main() {
num _convertStringToNum(dynamic str, String collection) {
if (str is String) { return num.tryParse(str); }
if (str is num) { return str; }
return null;
}
Map<String, dynamic> _convertIdFromStringToNum(Map<String, dynamic> map, String collection) {
final Map<String, dynamic> newMap = Map<String, dynamic>.from(map);
newMap.update('id', (mapId) => _convertStringToNum(mapId, collection));
return newMap;
}
Map<String, dynamic> dataMap = {
'types': [
{
'id': '123',
'name': 'foo',
},
{
'id': '234',
'name': 'bar',
}
],
};
dataMap.update('types', (types) {
if (!(types is List<Map<String, dynamic>>)) { return null; }
return types.map((Map<String, dynamic> type) => _convertIdFromStringToNum(type, 'types')).toList();
});
}
Following the map, having both key-value pair as dynamic, Write a logic to filter all the null values from Map without us?
Is there any other approach than traversing through the whole map and filtering out the values (Traversing whole map and getting Entry Object and discarding those pairs) ?
I need to remove all values that are null and return map
Map<String, dynamic> toMap() {
return {
'firstName': this.firstName,
'lastName': this.lastName
};
Use removeWhere on Map to remove entries you want to filter out:
void main() {
final map = {'text': null, 'body': 5, null: 'crap', 'number': 'ten'};
map.removeWhere((key, value) => key == null || value == null);
print(map); // {body: 5, number: ten}
}
And if you want to do it as part of your toMap() method you can do something like this with the cascade operator:
void main() {
print(A(null, 'Jensen').toMap()); // {lastName: Jensen}
}
class A {
final String? firstName;
final String? lastName;
A(this.firstName, this.lastName);
Map<dynamic, dynamic> toMap() {
return <dynamic, dynamic>{
'firstName': this.firstName,
'lastName': this.lastName
}..removeWhere(
(dynamic key, dynamic value) => key == null || value == null);
}
}
You can now use a map literal with conditional entries:
Map<String, dynamic> toMap() => {
if (firstName != null) 'firstName': firstName,
if (lastName != null) 'lastName': lastName,
};
I did this to make it easy remove nulls from map and list using removeWhere:
https://dartpad.dartlang.org/52902870f633da8959a39353e96fac25
Sample:
final data =
{
"name": "Carolina Ratliff",
"company": null,
"phone": "+1 (919) 488-2302",
"tags": [
"commodo",
null,
"dolore",
],
"friends": [
{
"id": 0,
"name": null,
"favorite_fruits": [
'apple', null, null, 'pear'
]
},
{
"id": 1,
"name": "Pearl Calhoun"
},
],
};
void main() {
// From map
print('Remove nulls from map:\n' + data.removeNulls().toString());
// From list
print('\nRemove nulls from list:\n' + [data].removeNulls().toString());
}
The limitation of removeWhere is that it does not check for nested values. Use this recursive solution if you want to remove all keys in the hierarchy.
dynamic removeNull(dynamic params) {
if (params is Map) {
var _map = {};
params.forEach((key, value) {
var _value = removeNull(value);
if (_value != null) {
_map[key] = _value;
}
});
// comment this condition if you want empty dictionary
if (_map.isNotEmpty)
return _map;
} else if (params is List) {
var _list = [];
for (var val in params) {
var _value = removeNull(val);
if (_value != null) {
_list.add(_value);
}
}
// comment this condition if you want empty list
if (_list.isNotEmpty)
return _list;
} else if (params != null) {
return params;
}
return null;
}
Example:
void main() {
Map<String, dynamic> myMap = {
"a": 1,
"b": 2,
"c": [
3,
4,
null,
{"d": 7, "e": null, "f": 5}
],
"g": {"h": null, "i": null},
"j": 6,
"h": []
};
print(removeNull(myMap));
}
Output:
{a: 1, b: 2, c: [3, 4, {d: 7, f: 5}], j: 6}
Note:
If you want an empty map and list when their child has null values, comment out an empty check for map and list in the code.
I suggest you to use removeWhere function
Map<String, dynamic> map = {
'1': 'one',
'2': null,
'3': 'three'
};
map.removeWhere((key, value) => key == null || value == null);
print(map);
Maybe, just like I did, someone might come looking, for How to remove null fields from a model that will be sent to the API using retrofit and dio in flutter
Here is what I used to remove null values from the Model object
#Body(nullToAbsent: true)
For example
#POST("/shops")
Future<Shop> createShop(#Body(nullToAbsent: true) Shop shop);
The nullToAbsent param does the trick
This is the method I am using with Flutter 3:
Map<String, Object?> removeAllNulls(Map<String, Object?> map) {
final data = {...map};
data.removeWhere((key, value) => value == null);
for (final entry in data.entries.toList()) {
final value = entry.value;
if (value is Map<String, Object?>) {
data[entry.key] = removeAllNulls(value);
} else if (value is List<Object?>) {
final list = List.from(value);
for (var i = 0; i < list.length; i++) {
final obj = list[i];
if (obj is Map<String, Object?>) {
list[i] = removeAllNulls(obj);
}
}
data[entry.key] = list;
}
}
return data;
}
Usage:
var jsonData = jsonDecode(jsonEncode(example.toJson()));
jsonData = removeAllNulls(jsonData);
Remove null from JSON Using Dart Extension Method
extension JsonExtension on Map<String, dynamic> {
Map<String, dynamic> get removeNull {
removeWhere((String key, dynamic value) => value == null);
return this;
}
}
final data = {
"hello":null,
"world":"test"
};
void main() {
print(data.removeNull);
}
Output
{world: test}
I tried two method to parse the rawData into dart objects. One using a for loop ads and it works but why _ads is not working when I use map ?
void main() {
dynamic rawData = [
{"title": "a", "id": 1}
];
List<Ad> ads = [];
for (var raw in rawData) {
Ad ad = Ad.fromJson(raw);
ads.add(ad);
}
print(ads);
List<Ad> _ads = rawData.map((e) => Ad.fromJson(e)).toList();
print(_ads);
}
class Ad {
Ad({
this.id,
this.title,
});
int id;
String title;
factory Ad.fromJson(Map<String, dynamic> json) => _$AdFromJson(json);
Map<String, dynamic> toJson() => _$AdToJson(this);
}
Ad _$AdFromJson(Map json) {
return Ad(
id: json['id'] as int,
title: json['title'] as String,
);
}
Map<String, dynamic> _$AdToJson(Ad instance) {
final val = <String, dynamic>{};
void writeNotNull(String key, dynamic value) {
if (value != null) {
val[key] = value;
}
}
writeNotNull('id', instance.id);
writeNotNull('title', instance.title);
return val;
}
The result of calling nearly any method on a dynamic defined variable are going to be dynamic since the Dart compiler are going through a difficult time guessing the type you want. So when you want the result to be saved into a variable with a specific type like List<Ad> _ads you really need to tell the compiler at each step what generic type you want and expect.
With that said, you can get you code to work by changing:
List<Ad> _ads = rawData.map((e) => Ad.fromJson(e)).toList();
Into:
List<Ad> _ads = rawData.map<Ad>((e) => Ad.fromJson(e)).toList();
And if you also want to make the analyzer happy:
List<Ad> _ads = rawData.map<Ad>((Map<String, dynamic> e) => Ad.fromJson(e)).toList() as List<Ad>;
i try to build a map to add it into firestore.
Produkt Class:
class Produkt{
String name;
int anzahl;
Produkt({
this.name,
this.anzahl,
});
factory Produkt.fromJson(Map<String, dynamic> parsedJson){
return Produkt(
name:parsedJson['Name'],
anzahl:parsedJson['Anzahl']
);
}
Map<String, dynamic> toProduktJson() =>
{
"Name" : name,
"Anzahl" : anzahl
};
}
ProduktList Class:
class ProduktList{
final List<Produkt> produkte;
ProduktList({
this.produkte,
});
factory ProduktList.fromJson(Map<String, dynamic> parsedJson){
var list = parsedJson["Produkte"] as List;
List<Produkt> produkte = list.map((i) => Produkt.fromJson(i.cast<String, dynamic>())).toList();
return ProduktList(
produkte: produkte,
);
}
Map<String, dynamic> toProdukteJson() =>
{
"Produkte" : [
produkte[0].toProduktJson(),
produkte[1].toProduktJson(),
produkte[2].toProduktJson(),
]
};
}
I wanted that the Map looks like:
{
"Produkte" : [
produkte[0].toProduktJson(),
produkte[1].toProduktJson(),
produkte[2].toProduktJson(),
]
};
But if the List produkte has a length of 2, the Map should have 2 and it the List have a length of 10, the Map should have 10 entries.
How can i do this?
Pls help me.
Thank you
An option would be the following:
Map<String, dynamic> toProdukteJson() {
Map map = new Map<String, dynamic>();
if (produkte != null) {
map["produkte"] = produkte.map((produkt) => produkt.toJson()).toList();
}
return map;
}
class Produkt {
final String id;
...
Produkt(this.id, ...);
Map toJson() => {'id' : id, ...};
}
var template = '/test/:key1/tester/:key2';
var filledIn = '/test/12/tester/3';
How can I get an map like this given inputs above?
[
key1: 12,
key2: 3
]
Here is a better solution that gives the exact format you asked for.
void main() {
final template = Template('/test/:key1/tester/:key2');
print(template.parse('/test/12/tester/3')); // {key1: 12, key2: 3}
}
class Template {
Template(this.source);
final String source;
List<String> _parts;
Iterable<int> _params;
List<String> get parts => _parts ??= source.split('/');
Iterable<int> get params => _params ??= parts
.where((part) => part.startsWith(':'))
.map((param) => parts.indexOf(param));
Map<String, String> parse(String string) {
final p = string.split('/');
return Map.fromEntries(
params.map((index) => MapEntry(parts[index].substring(1), p[index])));
}
}
The following works but it may not be the most elegant solution.
Map.from() is used because List.asMap() uses an Unmodifiable list.
final template = '/test/:key1/tester/:key2';
final filledIn = '/test/12/tester/3';
final params = Map.from(template.split('/').asMap())
..removeWhere((i, part) => !part.startsWith(':'));
final values = Map.from(filledIn.split('/').asMap())
..removeWhere((i, part) => !params.containsKey(i));
print(params.values); // [:key1, :key2]
print(values.values); // [12, 3]