Suppose I have a Map:
Map<String, int> source = {'a':1, 'b':2, 'c':3};
I want to get this:
Map<String, int> expected = {'a': 1, 'b':4, 'c':9 };
I want to achieve the result using map function:
Map<String,int> actual = source.map((key,value)=> {key: value * value});
However, I got this error:
The return type 'Map<String, int>' isn't a 'MapEntry<String, int>', as required by the closure's context
Can't we use the map function of map to get another map like this?
The mapping method should return a MapEntry instance since you can change both the key and value. So your code should instead be something like:
void main() {
final source = {'a': 1, 'b': 2, 'c': 3};
final actual = source.map((key, value) => MapEntry(key, value * value));
print(actual); // {a: 1, b: 4, c: 9}
}
Using collection-for might be more straightforward than using Map.map:
final source = {'a': 1, 'b': 2, 'c': 3};
final actual = {
for (var entry in source.entries)
entry.key: entry.value * entry.value,
};
Related
Minimal reproducible code:
bool _flag = true;
void main() {
if (_flag) {
apiFunction(a: 1, b: true, c: 'c');
} else {
apiFunction(a: 1, b: true);
}
}
// Some function that I can neither change nor read its default values.
void apiFunction({
int a = 0,
bool b = false,
String c = '',
}) {...}
As you can see based on flag, I need to pass c: 'c', and I'm currently duplicating apiFunction(a: 1, b: true) part. Is there any better way to write this?
Note: Please don't write this answer, because the default value is not given.
apiFunction(a: 1, b: true, c: _flag ? 'c' : '');
Real world scenario (optional)
For those who want to see this example in real world. When using cloud_firestore_odm, the generated abstract method update() accepts non-nullable value (if the non nullable type was used for a field) and the method looks like this:
Future<void> update({
int a,
bool b,
String c,
// other fields ...
});
Its implementation looks like:
Future<void> update({
Object? a = _sentinel,
Object? b = _sentinel,
Object? c = _sentinel,
// other fields ...
})
I am trying to understand the const keyword I found an article on GeeksforGeeks of const but I didn't understand it if we make the [1,2] constant then geek1 == geek2 will print true otherwise false how?
// Declaring a function
gfg() =>[1, 2]; // if we write here gfg() => const [1, 2]; then geek1 == geek2 will print true
// Main function
void main() {
// Assiging value
// through function
var geek1 = gfg();
var geek2 = gfg();
// Printing result
// true
print(geek1 == geek2);
print(geek1);
print(geek2);
}
== for arrays in dart compares references. Meaning that two arrays, although with identical contents can return false on comparison with ==.
const defines a compile-time constant, but it would also work with a class attribute. It's just important, that instead of creating a new array, the function returns the same reference twice.
The difference between the two functions is essentially, that in the first one, you define a new array with each call of the function (different reference each time), while in the second one you define a compile-time constant, that is returned on each call of the function (same reference each time).
So let's see some examples:
class Factory {
List<int> arr = [1, 2];
final finalArr = [1, 2];
static const constArr = [1, 2];
getValue() => [1, 2];
getInlineConst() => const [1, 2];
getRef() => arr;
getFinalRef() => finalArr;
getConstRef() => constArr;
}
void main() {
final f = new Factory();
print("values: ${f.getValue() == f.getValue()}");
print("const inline: ${f.getInlineConst() == f.getInlineConst()}");
print("reference: ${f.getRef() == f.getRef()}");
print("final reference: ${f.getFinalRef() == f.getFinalRef()}");
print("const reference: ${f.getConstRef() == f.getConstRef()}");
final refBefore = f.getRef();
f.arr = [1, 2];
final refAfter = f.getRef();
print("reference (with change inbetween): ${refBefore == refAfter}");
}
Output is:
values: false
const inline: true
reference: true
final reference: true
const reference: true
reference (with change inbetween): false
Dart canonicalizes constants. That means that const Foo(1) in one part of the program evaluates to the same object as const Foo(1) in another part of the program. There is only one object. That extends to constant lists too: const [1, 2, 3] will evaluate to the same constant list everywhere it occurs in the program.
Non-constant object creation creates a new object each time its evaluated: Foo(1) or new Foo(1) will create a new object each time. Same for list iterals.
So, identical(const [1, 2, 3], const [1, 2, 3]) is true, but identical([1, 2, 3], [1, 2, 3]) is false.
The == of Dart's built-in lists does not compare the contents of the list. It's inherited from Object and only checks whether it's the same object (basically using bool operator==(Object other) => identical(this, other);).
I would like to get the second to last item in a List, similarly than with getter last.
I tried the following :
final list = [1, 2, 3, 4, 5];
final secondToLast = (list..removeLast()).last; // Mutates the List
However it mutates the List.
There is many options available (however you should make sure that list is not null and has at least 2 elements) :
final list = [1, 2, 3, 4, 5];
// Works only for Lists
final secondToLast = list[list.length - 2];
final secondToLast = list.reversed.elementAt(1);
final secondToLast = list.reversed.skip(1).first;
// Works for any Iterable
final secondToLast = list.elementAt(list.length - 2);
To get something similar to last, you can write an extension on Iterable :
extension CustomIterable<T> on Iterable<T> {
T? get secondToLast {
return this == null || length < 2 ? null : elementAt(length - 2);
}
}
I want to have a map with duplicate keys. Is there such a map in Dart or a utility library that has this functionality?
I'm using the following get a count of items:
myList.forEach(
(element) {
if (!myMap.containsKey(element)) {
myMap[element] = 1;
} else {
myMap[element] += 1;
}
},
);
then convert keys/values to lists: Need to switch key/values...
final keys = myMap.keys.toList();
final itemSpit = keys.map((e) => e.toString().split('§º')).toList();
final values = myMap.values.toList();
put it in a map
final map = Map();
for (var i = 0; i < values.length; i++) {
map[values[i]] = itemSpit[i];
}
Obviously the keys are overridden in the for loop.
then
iterate over map (Put keys/values in flutter widgets)
final cells = map.entries
.map((e) => ........
THe first method increases the value count if there's a duplicate value. So I have this. ... {breadwhitelarge: 3, cornyellowsmall:5 ..etc..}
I then have to split the strings and have output like this
5 bread white large
3 corn yellow small
Instead of defining a map which allows duplicated keys you can instead create a Map<K,List<V>> like this example:
void main() {
final map = <String, List<int>>{};
addValueToMap(map, 'Test 1', 1);
addValueToMap(map, 'Test 1', 2);
addValueToMap(map, 'Test 2', 3);
addValueToMap(map, 'Test 1', 4);
addValueToMap(map, 'Test 2', 5);
addValueToMap(map, 'Test 3', 6);
print(map); // {Test 1: [1, 2, 4], Test 2: [3, 5], Test 3: [6]}
}
void addValueToMap<K, V>(Map<K, List<V>> map, K key, V value) =>
map.update(key, (list) => list..add(value), ifAbsent: () => [value]);
You can then ask for a given key and get a list of all values connected to this key.
package:quiver provides a MultiMap class with List-based and Set-based implementations.
Could you just create the Flutter widgets directly from the first map?
var widgets = [for (var e in myMap.entries) MyWidget(
count: e.value,
strings: [... e.key.split("§º")])];
Building the intermediate map seems to be what is causing the trouble.
this is an example map has duplicate keys
withDuplicateKey() {
List<dynamic> demoList = [
{1},
{2},
{3},
{1}
];
var toRemove = {};
demoList.forEach((e) {
toRemove.putIfAbsent(e, () => e);
});
print(toRemove.keys.toList());
}
output is ( printed list of key )
[{1}, {2}, {3}, {1}]
The Map interface doesn't seem to provide access to the entries as an iterable, nor does it expose a where method to filter entries. Am I missing something? Is there a simple workaround?
e.g.
Map map;
final filteredMap = map.where((k, v) => k.startsWith("foo"));
Update: with control flow collection statements you can also do this:
final filteredMap = {
for (final key in map.keys)
if (!key.startsWith('foo')) key: map[key]
};
Original answer: Dart 2.0.0 added removeWhere which can be used to filter Map entities. Given your example, you could apply this as:
Map map;
final filteredMap = Map.from(map)..removeWhere((k, v) => !k.startsWith("foo"));
It's not the where method you asked for, but filtering Map entities is certainly doable this way.
Since Dart 2.0 Maps have an entries getter that returns an Iterable<MapEntry<K, V>> so you can do:
MapEntry theOne = map.entries.firstWhere((entry) {
return entry.key.startsWith('foo');
}, orElse: () => MapEntry(null, null));
You can use
library x;
void main(List<String> args) {
Map map = {'key1': 'aölsjfd', 'key2': 'oiweuwrow', 'key11': 'oipoip', 'key13': 'werwr'};
final filteredMap = new Map.fromIterable(
map.keys.where((k) => k.startsWith('key1')), key: (k) => k, value: (k) => map[k]);
filteredMap.forEach((k, v) => print('key: $k, value: $v'));
}
I use dartx and it's filter method
var myMap = {
"a": [1, 2, 3],
"b": [4, 5, 6],
"c": [7, 8, 9],
};
var result = myMap.filter((entry) => entry.key != "a");
You can just create an extension function and then use it anywhere in your code.
Put this in any file (I called mine MapUtils.dart)
extension MapUtils<K, V> on Map<K, V> {
Map<K, V> where(bool Function(K, V) condition) {
Map<K, V> result = {};
this.entries.forEach((element) {
if (condition(element.key, element.value)) {
result[element.key] = element.value;
}
});
return result;
}
}
and then use it like so:
Map<String, int> peopleHeight = {"Bob":170, "Alice":130};
Map<String, int> shortPeople = peopleHeight.where((name, height) => height < 140);