How to prevent code duplication in Dart functions? - dart

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

Related

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

Dart map to another map through map function

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,
};

Is there anything like a struct in dart?

In javascript it always bothered me people use objects as vectors like {x: 1, y: 2} instead of using an array [1,2]. Access time for the array is much faster than the object but accessing by index is more confusing especially if you need a large array. I know dart has fixed arrays but is there a way to name the offsets of an array like you would a struct or a tuple/record in another language? Define enum/constants maybe?
I'd want something like
List<int> myVector = new List([x,y]);
myVector.x = 5;
is there an equivalent or idiomatic way to do this?
That sounds like a class.
class MyVector {
int x;
int y;
MyVector(this.x, this.y);
}
There is no simpler and more efficient way to create a name-indexed structure at runtime. For simplicity you could usually use a Map, but it's not as efficient as a real class.
A class should be at least as efficient (time and memory) as a fixed length list, after all it doesn't have to do an index bounds check.
In Dart 3.0, the language will introduce records. At that point, you can use a record with named fields instead of creating a primitive class:
var myVector = (x: 42, y: 37);
print(myVector.x);
A record is unmodifiable, so you won't be able to update the values after it has been created.
For me, i see 2 way to do this. I will sort by best in my point of view
Class based method
Here, the approach is to encapsulate your need, in a dedicated object
Pros:
It's encapsultate
You can propose several way to access variable, depend of the need
You can extend functionality without break everything
I love it :p
Cons
More time spend to create class, etc.
Do you really need what i say in pros ?
Maybe weird for js people
example :
class Vector {
int x;
int y;
static final String X = "x";
static final String Y = "y";
Vector({this.x, this.y});
Vector.fromList(List<int> listOfCoor) {
this.x = listOfCoor[0];
this.y = listOfCoor[1];
}
// Here i use String, but you can use [int] an redefine static final member
int operator[](String coor) {
if (coor == "x") {
return this.x;
} else if (coor == "y") {
return this.y;
} else {
// Need to be change by a more adapt exception :)
throw new Exception("Wrong coor");
}
}
}
void main() {
Vector v = new Vector(x: 5, y: 42);
Vector v2 = new Vector.fromList([12, 24]);
print(v.x); // print 5
print(v["y"]); // print 42
print(v2.x); // print 12
print(v2[Vector.Y]); // print 24
}
Enum based method:
You can also defined a "enum" (actually not really implement but will be in the future version) that will contains "shortcut" to your value
Pros
More simple to implement
Is more like your example ;p
Cons
Less extendable
i think is not very pretty
Not OOP think
example:
class Vector {
static final int x = 0;
static final int y = 1;
}
void main() {
List<int> myVector = new List(2);
myVector[Vector.x] = 5;
myVector[Vector.y] = 42;
}
Make your choice ;p
This is only possible with a class in Dart.
There are some open feature requests at http://dartbug.com
introduce struct (lightweight class)
Give us a way to structure Bytedata
If you have reasonably big data structure, you can use "dart:typed_data" as a model and provide lightweight view for the stored data. This way the overhead should be minimal.
For example, if you need 4X4 matrix of Uint8 values:
import "dart:typed_data";
import "dart:collection";
import "package:range/range.dart";
class Model4X4Uint8 {
final Uint8List _data;
static const int objectLength = 4 * 4;
final Queue<int> _freeSlotIndexes;
Model4X4Uint8(int length): _data = new Uint8List((length) * objectLength),
_freeSlotIndexes = new Queue<int>.from(range(0, length));
int get slotsLeft => _freeSlotIndexes.length;
num operator [](int index) => _data[index];
operator []=(int index, int val) => _data[index] = val;
int reserveSlot() =>
slotsLeft > 0 ? _freeSlotIndexes.removeFirst() : throw ("full");
void delete(int index) => _freeSlotIndexes.addFirst(index);
}
class Matrix4X4Uint8 {
final int offset;
final Model4X4Uint8 model;
const Matrix4X4Uint8(this.model, this.offset);
num operator [](int index) => model[offset + index];
operator []=(int index, int val) => model[offset + index] = val;
void delete() => model.delete(offset);
}
void main() {
final Model4X4Uint8 data = new Model4X4Uint8(100);
final Matrix4X4Uint8 mat = new Matrix4X4Uint8(data, data.reserveSlot())
..[14] = 10
..[12] = 256; //overlow;
print("${mat[0]} ${mat[4]} ${mat[8]} ${mat[12]} \n"
"${mat[1]} ${mat[5]} ${mat[9]} ${mat[13]} \n"
"${mat[2]} ${mat[6]} ${mat[10]} ${mat[14]} \n"
"${mat[3]} ${mat[7]} ${mat[11]} ${mat[15]} \n");
mat.delete();
}
But this is very low level solution and can easily create sneaky bugs with memory management and overflows.
You could also use an extension on List to create aliases to specific indexes.
Although it will be difficult to set up mutually exclusive aliases, in some cases, it may be a simple solution.
import 'package:test/test.dart';
extension Coordinates<V> on List<V> {
V get x => this[0];
V get y => this[1];
V get z => this[2];
}
void main() {
test('access by property', () {
var position = [5, 4, -2];
expect(position.x, 5);
expect(position.y, 4);
expect(position.z, -2);
});
}
The Tuple package https://pub.dev/packages/tuple might be what you are looking for when a class is too heavy.
import 'package:tuple/tuple.dart';
const point = Tuple2<int, int>(1, 2);
print(point.item1); // print 1
print(point.item2); // print 2

What are named arguments compiled equivalents in JS?

If I say something like
void simple({foo: true, bar: false}){
print('$foo $bar');
}
Does it compile down to
function simple(foo, bar) ...
or does it turn the named arguments so it just accepts an 1 object as an argument containing object.foo and object.bar. I looked at the dart.js it generated but it looks like it's compiling the AST of the program from the javascript which is slightly insane to me.
After some research, i have found that, as say Günter Zöchbauer, a lot of optimization are done.
But, due to this optimization, the compiler avoid (as i see in my experimentation) passing object when it can. I think is for performance but i'm not sure
So in most of the time, Named argment are passed directly, and resolved during the compilation.
To test this, i have write a dummy code :
dummy.dart
import 'dart:math';
double calcFunc({int a, int b}) {
return (a * b / (a+b)) * (new Random().nextInt(100)) ;
}
String myFunc({int a: 0, int b: 0}) {
return "Numbers : ${a + calcFunc(a: a, b : b) * (new Random().nextInt(100))}";
}
void main() {
print("Func call: ${myFunc(a: 42, b: 24)}");
print("Func 2nd call: ${myFunc(a: 21)}");
}
and the result JS for the code (without all the thing generated)
dummy.dart.js
["", "main.dart", , F, {
"^": "",
myFunc: function(a, b) {
var t1 = C.C__JSRandom.nextInt$1(100);
return "Numbers : " + (a + a * b / (a + b) * t1 * C.C__JSRandom.nextInt$1(100));
},
main: function() {
P.print("Func call: " + F.myFunc(42, 24));
P.print("Func 2nd call: " + F.myFunc(21, 0));
}
},
1],
]);
as you can this here, no object are passed in parameter, but they are directly replaced.

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