Packing/Unpacking arguments in Dart [duplicate] - dart

This question already has answers here:
Creating function with variable number of arguments or parameters in Dart
(6 answers)
Closed 5 years ago.
Is there a way in Dart to pack/unpack arguments in a function (like Python for example) ?
For packing example, being able to declare a function like this :
packArguments(*listOfArguments, **mapOfArguments) {
listOfArguments.forEach((arg) => print(arg));
mapOfArguments.forEach((key, val) => print("$key => $val"));
}
And then doing this :
packArguments("I", "Put", "whatever", "I", "want, arg1: "A", arg2: 1);
Would display :
I
Put
whatever
I
want
arg1 => A
arg2 => 1
As for unpacking, being able to do something like that :
functionWithLotOfArgument(a, b, c, d, e, {aa, bb = null, cc = null}) {
// do stuff
}
var argList = [1, 2, 3, 4, 5];
var argMap = {"aa": "haha", bb: "baby"};
functionWithLotOfArgument(*argList, **argMap);
Related issue https://github.com/dart-lang/sdk/issues/29087

This is not currently supported, but you can very easily pack yourself, by passing a list & map:
void packArguments(List listOfArguments, Map mapOfArguments) {
listOfArguments.forEach((arg) => print(arg));
mapOfArguments.forEach((key, val) => print("$key => $val"));
}
void main() {
packArguments(['a', 3], {'arg1': 'a', 'arg2': 5});
}
https://dartpad.dartlang.org/98ed3a3b07a2cca049cde69ca50ca269

Related

How to prevent code duplication in Dart functions?

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 ...
})

Using whitespace as a list separator

I can successfully parse a comma-delimited list with dart-petitparser, but the same code fails when confronted with a space-delimited list:
class MyDefinition extends GrammarDefinition {
#override
Parser start() => throw UnsupportedError('Not yet.');
Parser commas() =>
ref0(number).separatedBy(char(','), includeSeparators: false);
Parser spaces() =>
ref0(number).separatedBy(char(' '), includeSeparators: false);
Parser<int> number() => digit().plus().flatten().trim().map(int.parse);
}
test('commas', () {
var defn = MyDefinition();
var parser = defn.build(start: defn.commas);
expect(parser.parse("7,4,9,5").value, [7, 4, 9, 5]); // PASS
});
test('spaces', () {
var defn = MyDefinition();
var parser = defn.build(start: defn.spaces);
expect(parser.parse("7 4 9 5").value, [7, 4, 9, 5]); // FAIL
});
It fails with the following:
Expected: [7, 4, 9, 5]
Actual: [7]
Which: at location [1] is [7] which shorter than expected
What am I doing wrong? I've dug through a bunch of the example grammars, and didn't find a whitespace-delimited list anywhere.
The .trim() parser in your number parser is consuming whitespaces before and after the number. This prevents the space-delimited list parser from finding a separating whitespace.
I suggest to rewrite as:
Parser spaces() =>
ref0(number).separatedBy(char(' ').plus(), includeSeparators: false);
// add this ^^^^^^^
Parser<int> number() =>
digit().plus().flatten().map(int.parse);
// remove trim, here ^

Const keyword problem if we make the [1,2] constant then geek1 == geek2 will print true otherwise false how?

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);).

Map implementation with duplicate keys in Dart

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}]

How can I filter a map / access the entries

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);

Resources