Dart Map syntax: putIfAbsent('FirstKey',() => 1); What is: ()? - dart

In editor hint you get:
scores.putIfAbsent(key, () => numValue);
I am adding single "rows" to my Maps with commands:
myMap.putIfAbsent(9, () => 'Planned');
yourMap.putIfAbsent('foo', () => true);
so: what does that () mean ?

The putIfAbsent function takes two arguments, the key and a function that will return the new value, if needed.
See https://api.dartlang.org/apidocs/channels/stable/dartdoc-viewer/dart-core.Map#id_putIfAbsent
The reason for the second argument to be a function returning the new value, and just not the value itself, is that if the map already contains the key, it's sometimes undesirable to create the value.
Consider the following example:
var map = ...;
var key = ...;
map.putIfAbsent(key, new Value());
If map already contains key, the new value object is not used at all. If the Value object is some heavy or expensive to allocate object, this is an unwanted side-effect.
By taking a function instead
var map = ...;
var key = ...;
map.putIfAbsent(key, () => new Value());
It will only execute the function if the key is not present in map, and the value is needed.
So, to answer the syntax question. A expression of the form () => ... is a short hand of a function expression, returning the result of the first expression. A small example:
var function = () => "some string";
var str = function();
print(str);
will print "some string".

() can't be seen alone here its () => that makes the difference.
// adds the value of numValue
scores.putIfAbsent(key, numValue);
// adds an (anonymous) function that takes 0 arguments and returns the value of numValue.
scores.putIfAbsent(key, () => numValue);
So this two forms are totally different. The first adds a value and the second adds a function.
When we assume numValue has the value 5 when putIfAbsent is called then in the first case
var x = scores[key];
// here var x has the value 5
and in the second case
var x = scores[key];
// here var x references a function
var y = x();
// calling the function executes its body which is `return numValue` (the `return` is implicit in the shorthand function form)
// here var y has the value 5

() indicates that the expression doesn't have any input parameters.

Related

Is there a way to pass an argument to the `test` function of the `firstWhere` method of an iterable

I am learning Dart and I'm following the Codelabs tutorial on iterable collections.
I have just read about the firstWhere method of iterables for finding the first element that satisfies some criterion.
The tutorial gives an example similar to the following:
bool predicate(String item, {int minLength = 6}) => item.length > minLength;
void main() {
const items = ['Salad', 'Popcorn', 'Toast', 'Lasagne'];
var foundItem = items.firstWhere(predicate);
print(foundItem);
}
Which would print Popcorn as it is the first string with 6 or more characters.
I'm wondering whether it is possible to pass the minLength argument to predicate when calling items.firstWhere(predicate).
sure, but like this:
final minLength = 6;
final foundItem = items.firstWhere((String item) => item.length > minLength));
what you example is doing is just extracting the method (String item) => item.length > minLength; to a separate global variable. which isn't necessary and I wouldn't recommend.

Searching a List of objects for a particular object in dart using "where"

I would like to obtain an object from a List based on a specific search criteria of its member variable
this is the code I am using
class foo
{
foo(this._a);
int _a;
}
List<foo> lst = new List<foo>();
main()
{
foo f = new foo(12);
lst.add(f);
List<foo> result = lst.where( (foo m) {
return m._a == 12;
});
print(result[0]._a);
}
I am getting the error and not sure how to resolve this
Uncaught exception:
TypeError: Instance of 'WhereIterable<foo>': type 'WhereIterable<foo>' is not a subtype of type 'List<foo>'
I am trying to search for an object whose member variable a == 12. Any suggestions on what I might be doing wrong ?
The Iterable.where method returns an iterable of all the members which satisfy your test, not just one, and it's a lazily computed iterable, not a list. You can use lst.where(test).toList() to create a list, but that's overkill if you only need the first element.
You can use lst.firstWhere(test) instead to only return the first element, or you can use lst.where(test).first to do effectively the same thing.
In either case, the code will throw if there is no element matched by the test.
To avoid throwing, you can use var result = lst.firstWhere(test, orElse: () => null) so you get null if there is no such element.
Another alternative is
foo result;
int index = lst.indexWhere(test);
if (index >= 0) result = lst[index];
The answer is simple. Iterable.where returns an Iterable, not a List. AFAIK this is because _WhereIterable does its computations lazily.
If you really need to return a List, call lst.where(...).toList().
Otherwise, you can set result to be an Iterable<foo>, instead of a List<foo>.
or you can go crazy and do this:
bool checkIfProductNotFound(Map<String, Object> trendingProduct) {
bool isNotFound = this
._MyProductList
.where((element) => element["id"] == trendingProduct["id"])
.toList()
.isEmpty;
return isNotFound ;
}

Calling a function with two same arguments, but setting the second while calling

I am calling a function that expects two arguments. I use the same variable, but at the second argument I set this variable to another thing.
See below:
https://dartpad.dartlang.org/2156442de07f56d90b430bc67f3461ac
void main() {
String s = 'oi';
aa(s, s = 'oi2');
}
void aa(String buf, String buf2){
print('$buf, $buf2');
}
This will print "oi, oi2".
I want this to happen. I am using a modified notification within properties, like:
set title(String n) {
this.modified('title', _title, _title = n);
}
However, I wonder if this can be seen as a bug or it is expected.
thanks, Joe
s is a String which are passed by value, not by reference.
aa(s, s = 'oi2');
evaluates the first parameter s, which is 'oi'
next s = 'oi2' is evaluated, which means s gets 'oi2' assigned
then the result of s = 'oi2' (which is 'oi2') is passed as 2nd parameter.
After aa(s, s = 'oi2'); s has the value oi2.
See also https://gist.github.com/floitschG/b278ada0316dca96e78c1498d15a2bb9
Evaluation order of arguments is left-to-right, so you can rely on the first argument's value being found by evaluation s to "ii", and then second argument's value is the value of the assignment s = 'oi2 - which evaluates to "oi2" (and not, technically, by reading the variable, it just happens that the variable is written to with the same value before the function is called).
It is expected - if any implementation does something else, it's broken.

Breeze import with entity extended property of ko.observableArray() throwing error

Running into an error during import for entities extended with a ko.observableArray() property, vs. being extended as a simple array [] type in the constructor.
var customerCtor = function () {
this.extendedProp = ko.observable(true);
//this.extendedArray = ko.observableArray(); // causes error: Cannot write a value to a ko.computed unless you specify a 'write' option. If you wish to read the current value, don't pass any parameters.
this.extendedArray = []; // this works just fine
};
I created a test along-side the Breeze v1.3.6 DocCode: exportImportTests.js "stash entire cache locally and restore" as my starting point, and here is the new test:
test("w/extended Customer, stash entire cache locally and restore", 3, function () {
var em1 = newEm();
var store = em1.metadataStore;
// extend Customer with observables
var customerCtor = function () {
this.extendedProp = ko.observable(true);
this.extendedArray = ko.observableArray(); // causes error: Cannot write a value to a ko.computed unless you specify a 'write' option. If you wish to read the current value, don't pass any parameters.
//this.extendedArray = []; // but this will work just fine?
};
store.registerEntityTypeCtor("Customer", customerCtor);
var expected = testData.primeTheCache(em1);
// grab first Customer, push value onto extendedArray prop
var custEntity = em1.getEntities(expected.customerType)[0];
custEntity.extendedArray().push('some-value'); // even when defined as [], Breeze re-writes field as ko.observable
var exportData = em1.exportEntities();
var stashName = "stash_everything";
window.localStorage.setItem(stashName, exportData);
var importData = window.localStorage.getItem(stashName);
var em2 = new EntityManager(); // virginal - so register ctor on this instance
var store2 = em2.metadataStore;
store2.registerEntityTypeCtor("Customer", customerCtor);
em2.importEntities(importData);
var entitiesInCache = em2.getEntities();
var restoreCount = entitiesInCache.length;
equal(restoreCount, expected.entityCount,
"should have restored expected number of all entities");
var restoredCustomer = em2.getEntities(expected.customerType)[0];
ok(restoredCustomer.extendedProp(), 'extended property present');
ok(restoredCustomer.extendedArray().length > 0, 'extended Array present and has data');
});
An em2.importEntities(importData); throws the error:
Error: Cannot write a value to a ko.computed unless you specify a 'write' option. If you wish to read the current value, don't pass any parameters.
at Error (<anonymous>)
at h [as extendedArray] (http://localhost:47595/Scripts/knockout-2.2.1.js:44:167)
at ctor.initializeEntityPrototype.proto.setProperty (http://localhost:47595/Scripts/breeze.debug.js:14634:31)
at updateTargetPropertyFromRaw (http://localhost:47595/Scripts/breeze.debug.js:13062:24)
at aspectName (http://localhost:47595/Scripts/breeze.debug.js:13025:13)
at Array.forEach (native)
at updateTargetFromRaw (http://localhost:47595/Scripts/breeze.debug.js:13023:19)
at em._inKeyFixup (http://localhost:47595/Scripts/breeze.debug.js:12601:17)
at Array.forEach (native)
at importEntityGroup (http://localhost:47595/Scripts/breeze.debug.js:12568:28)
As Breeze always rewrites constructor fields (in my case for KO), defining as [] works. But not sure why this would happen when the property is pre-defined?
Anyone run into this, or have I missed a doc note somewhere?
We'll look at it.
Yes, Breeze assumes every property added in a constructor is supposed to be rewritten per the prevailing "model library" which, in your case, is KO. Therefore, no surprise that the array becomes a ko.observableArray.
Moreover, because such a property is presumed to be under Breeze's jurisdiction, we have to tie it into Breeze observability and serialization mechanisms which means we re-write it as a Breeze-flavored observable array. Such an array is a computed.
Evidently there is some problem with the way we're doing this for a property which is "unmapped". We'll look at that.
N.B.: I am assuming (and your code confirms) that the array property, extendedArray, is an "unmapped property" in the Breeze sense. That should be fine.
You should not mention mapped collection navigation properties in your constructor. There is no valid reason to do so that I can think of. The main reason to mention a mapped property in the constructor is (a) to give it a default value or (b) make it available to a custom (unmapped) computed property. There is no reasonable alternative default value for a collection navigation property (empty is the default) and it would be rare/avoidable to include it in a computed.

Seek overview of the '=>' operator

I'm browsing sample asp.net code in an MVC project and need a better understanding of the => operator. Plugging => into search engines is non-helpful.
thx
The => syntax creates lambda expressions, which are small functions.
For example, the line
Func<int, int> myFunc = i => 2 * i;
declares a variable of type Func<int, int> (a delegate that takes one integer and returns another one), and assigns it to a lambda expressions that takes a parameter called i (the compiler automatically figures out that i is an int) and returns 2 * i.
Yo can search it as lambda. See here http://msdn.microsoft.com/en-us/library/bb397687.aspx
The => operator, as has been mentioned, represents a lambda expression. This is short-hand for an anonymous delegate. Here is practical example:
If you want to filter all Person objects in a collection to return only Male people, the Where() extension method requires a Func delegate you could create a named delegate like this:
Func<Person, bool> isMale = delegate(Person peep) { return peep.Gender == "male"; };
var men = from p in peeps.Where(isMale)
select p;
Or you could use an anonymous delegate like this:
var women = from p in peeps.Where(delegate(Person peep) { return peep.Gender != "male"; })
select p;
The lambda allows you to declare the anonymous delegate using a short-hand, like this:
var women = from p in peeps.Where(x => x.Gender != "male")
select p;
Notice the correspondence between delegate(Person peep) and x, and between 'return peep.Gender != "male"and 'x.Gender != "male".

Resources