I have a question on how to easily use map as a parameter in Dart. Is there any easy way of passing all the key-values pairs of a map object to a function?
For example, I have a map and a function like this:
const testMap = {"a": 1, "b":2};
int testFunc(a, b){
return a + b;
}
and I want to use them like this:
testFunct(**testMap) // not possible in dart (though possible in python)
which should give us 3 as the result.
Any smart solution like this? Thanks!
Please note that ** is the python-way of passing the parameters inside dictionary: docs
It's not directly possible the way you write it, but you can do something similar using Function.apply.
You have to either pass the arguments as positional, and then you need to know the order:
const testMap = {"a": 1, "b":2};
int testFunc(a, b){
return a + b;
}
void main() {
print(Function.apply(testFunc, testMap.values.toList()));
}
or you have to use named parameters and then the map keys should be symbols:
const testMap = {#a: 1, #b:2};
int testFunc({required int a, required int b}){
return a + b;
}
void main() {
print(Function.apply(testFunc, [], testMap));
}
Dart distinguishes positional and named parameters, so if you want to refer to a parameter by name, it should be named.
The former approach is dangerous because you don't use the names of the map. It should really just be const testArguments = [1, 2]; and not include a map which doesn't help you.
Using Function.apply is not type safe. It's as type-safe as calling something with type dynamic or Function — which means not safe at all — so use it carefully and only when necessary.
Related
The code below defines a generic myFirstWhereFunction with 3 arguments:
Generic list
Generic value to search in the list
Generic default value to return if the searched value is not in the passed generic list
The code:
void main() {
const List<int> intLst = [1, 2, 3, 4];
print(myFirstWhereFunc(intLst, 4, -1));
print(myFirstWhereFunc(intLst, 5, -1));
const List<String> strLst = ['coucou', 'go', 'bold', 'tooltip'];
print(myFirstWhereFunc(strLst, 'go', 'not exist'));
print(myFirstWhereFunc(strLst, 'ok', 'not exist'));
}
T myFirstWhereFunc<T>(List<T> lst, T searchVal, T defaultVal) {
return lst.firstWhere((element) => element == searchVal, orElse: <T> () {
return defaultVal;
});
}
But this code generates an exception.
One solution is to replace the generic myFirstWhereFunc return type by dynamic (code below):
dynamic myFirstWhereFunc<T>(List<T> lst, T searchVal, T defaultVal) {
return lst.firstWhere((element) => element == searchVal,
orElse: () => defaultVal);
}
But is there another way of solving the problem ?
I believe that the problem is that when you do:
print(myFirstWhereFunc(intLst, 4, -1));
there are two possible ways to infer the type of myFirstWhereFunc:
Bottom-up (inside-out): myFirstWhereFunc is called with a List<int> and with int arguments, so its type could be myFirstWhereFunc<int>. This is what you want.
Top-down (outside-in): print has an Object? parameter, so myFirstWhereFunc could be myFirstWhereFunc<Object?> so that it returns an Object?. This is what actually happens and is what you do not want.
Dart ends up with two possible ways to infer the generic type parameter, both seem equally valid at compilation-time, and it picks the one that you happen to not want. Picking the other approach likely would result in undesirable outcomes for different code examples. Arguably inference could try both ways and pick the narrower type if one approach leads to a subtype of the other. I'm not sure offhand if that would break code, but I wouldn't be surprised. (I also suspect that it's been suggested in https://github.com/dart-lang/language/issues somewhere...)
Changing myFirstWhereFunc's return type to dynamic is a crude workaround to the problem because it makes myFirsyWhereFunc's type no longer inferrable from print's parameter type.
If you split the line up and use a temporary variable, inference can infer myFirstWhereFunc independently of print and then should do what you want:
var intResult = myFirstWhereFunc(intLst, 4, -1);
print(intResult);
Supposing that I have a List and a Map:
Map<String, dynamic> rootMap = {
'a': 'b',
'c': ['d', ['e', 'f']],
'g': {'h': 'i', 'j': {'k': 'l'}}
};
List<dynamic> rootList = [
11, 22, [33, 44, [55, 66]]
];
...which means I can access the value 'l' in rootMap as such:
var valueL = rootMap['g']['j']['k'];
...which also means I can access the value 66 in rootList as such:
var value66 = rootList[2][2][2]
However, assuming the user will provide something like a path to data, let's say, in a List form as such:
List<dynamic> dataPathForRootMap = ['g', 'j', 'k'];
List<dynamic> dataPathForRootList = [2, 2, 2];
I would like to use these lists (paths for data) in order to access the data in rootMap and rootList.
Note that both are List<dynamic> because the data might be complicated, meaning that there might be Lists under Map or Maps under Lists.
I came up with a solution as such:
var resolvedRootMapData = rootMap;
for (var path in dataPathForRootMap) {
resolvedRootMapData = resolvedRootMapData[path]
}
// at the end, the value of `resolvedRootMapData` will be `l`
You can apply the same logic to rootList as well. I want to focus on rootMap.
I do not like this approach for a couple of reasons:
This is not null safe. The user might provide a false path data and it would fail. Of course, this can be avoidable but still...
This does not look quite idiomatic.
The type of resolvedRootMapData is inconsistent on each for-iteration.
So, the question is:How do I get a nested element in a List or a Map with a provided List in Dart? Is there a solution to this that I am not aware of maybe?
Thanks in advance.
Environment
The output of flutter --version:
Flutter 2.8.1 • channel stable • https://github.com/flutter/flutter.git
Framework • revision 77d935af4d (3 weeks ago) • 2021-12-16 08:37:33 -0800
Engine • revision 890a5fca2e
Tools • Dart 2.15.1
The code that you don't like is probably the best you can do with the data you have.
As you point out, the types of the individual lists or maps that you traverse are not the same, but the input array of indices is absent of any type information, so you will need to do dynamic lookups. There is no way to make this well-typed (enough to compile) without dynamic lookups or casts.
If the data are heterogenous in themselves, it's probably impossible to do anything.
If the data are at least typeable without using dynamic or Object, then it's technically possible to define a typed path that preserves type safety. (Not null safety, you're never safe when lookup up in a map.)
If you really want to do type safe lookups, the following is an example of such an approach. It uses reverse ordering of lookups, because it's much much easier that way (because it's effectively building as stack).
abstract class Lookup<T, V> {
Lookup<List<T>, V> operator [](int index) =>
ListLookup<T, V>(this, index);
Lookup<Map<K, T>, V> get<K>(K key) =>
MapLookup<K, T, V>(this, key);
V call(T structure);
}
class ValueLookup<T> extends Lookup<T, T> {
T call(T value) => value;
}
class ListLookup<T, V> extends Lookup<List<T>, V> {
final int _index;
final Lookup<T, V> _rest;
ListLookup(this._rest, this._index);
V call(List<T> list) => _rest(list[_index]);
}
class MapLookup<K, T, V> extends Lookup<Map<K, T>, V> {
final K _key;
final Lookup<T, V> _rest;
MapLookup(this._rest, this._key);
V call(Map<K, T> map) => _rest(map[_key] as T);
}
Lookup<V, V> lookup<V>() => ValueLookup<V>();
Then you can write the path [1]["foo"][2] as
void main(){
List<Map<String, List<int>>> structure = [{}, {"foo": [0, 0, 42]}];
var path = lookup<int>()[2].get("foo")[1]; // path [1]["foo"][2]
// Type: Lookup<List<Map<String, List<int>>>>
var result = path(structure); // 42
print(result);
}
Most likely, that's massive overkill for what you need.
Try to figure out what your precise needs are, and whether a more object oriented data structure will better serve you.
I am trying to create a custom collector in order to count valid elements of a list. I have done it using one of the already provide collectors:
arr.stream()
.filter(e -> e.matches("[:;][~-]?[)D]"))
.map(e -> 1)
.reduce(0, Integer::sum);
but as a challenge for myself, I wanted to create my own custom collector in order to understand it better. And this is where I got stuck.
It is probably something trivial but I am learning this and can't figure a supplier, an accumulator, and a combiner. I guess I still don't understand something about them. For instance, I have a similar stream:
arr1.stream()
.filter(e -> e.matches("[:;][~-]?[)D]"))
.map(e -> 1)
.collect(temporary array, adding to array, reduce);
AFAIK supplier is a function without arguments, which returns something. I studied standard examples and it is usually a method reference for a new collection, e.g. ArrayList::new. I tried to use constant 0, e -> 0 because I want to return a scalar. I think it is wrong because it makes the stream returning 0. If using method reference for a temporary collection, Java complains about a mismatch of types of a supplier and returning object. I am also confused about using an accumulator if the final result is a number as a combiner would reduce all elements to a number, e.g. (a,b) -> a + b`.
I'm completely stumped.
Probably part of your problem is that you cannot obviously create an accumulator for an Integer type since it is immutable.
You start with this:
System.out.println(IntStream.of(1,2,3).reduce(0, Integer::sum));
You can extend to this:
System.out.println(IntStream.of(1,2,3).boxed()
.collect(Collectors.reducing(0, (i1,i2)->i1+i2)));
Or even this, which has an intermediate mapping function
System.out.println(IntStream.of(1,2,3).boxed()
.collect(Collectors.reducing(0, i->i*2, (i1,i2)->i1+i2)));
You can get this far with your own Collector
Collector<Integer, Integer, Integer> myctry = Collector.of(
()->0,
(i1,i2)->{
// what to do here?
},
(i1,i2)->{
return i1+i2;
}
);
The accumulator is A function that folds a value into a mutable result container with mutable being the keyword here.
So, make a mutable integer
public class MutableInteger {
private int value;
public MutableInteger(int value) {
this.value = value;
}
public void set(int value) {
this.value = value;
}
public int intValue() {
return value;
}
}
And now:
Collector<MutableInteger, MutableInteger, MutableInteger> myc = Collector.of(
()->new MutableInteger(0),
(i1,i2)->{
i1.set(i1.intValue()+i2.intValue());
},
(i1,i2)->{
i1.set(i1.intValue()+i2.intValue());
return i1;
}
);
And then:
System.out.println(IntStream.of(1,2,3)
.mapToObj(MutableInteger::new)
.collect(myc).intValue());
Reference:
Example of stream reduction with distinct combiner and accumulator
EDIT: The finisher just does whatever with the final result. If you don't set it on purpose then it is set by default to IDENTITY_FINISH which is Function.identity() which says just to return the final result as is.
EDIT: If you're really desperate:
Collector<int[], int[], int[]> mycai = Collector.of(
()->new int[1],
(i1,i2)->i1[0] += i2[0],
(i1,i2)->{i1[0] += i2[0]; return i1;}
);
System.out.println(IntStream.of(1,2,3)
.mapToObj(v->{
int[] i = new int[1];
i[0] = v;
return i;
})
.collect(mycai)[0]);
I am trying to use a HashMap of Lists of strings in Vala, but it seems the object lifecycle is biting me. Here is my current code:
public class MyClass : CodeVisitor {
GLib.HashTable<string, GLib.List<string>> generic_classes = new GLib.HashTable<string, GLib.List<string>> (str_hash, str_equal);
public override void visit_data_type(DataType d) {
string c = ...
string s = ...
if (! this.generic_classes.contains(c)) {
this.generic_classes.insert(c, new GLib.List<string>());
}
unowned GLib.List<string> l = this.generic_classes.lookup(c);
bool is_dup = false;
foreach(unowned string ss in l) {
if (s == ss) {
is_dup = true;
}
}
if ( ! is_dup) {
l.append(s);
}
}
Note that I am adding a string value into the list associated with a string key. If the list doesn't exist, I create it.
Lets say I run the code with the same values of c and s three times. Based some printf debugging, it seems that only one list is created, yet each time it is empty. I'd expect the list of have size 0, then 1, and then 1. Am I doing something wrong when it comes to the Vala memory management/reference counting?
GLib.List is the problem here. Most operations on GLib.List and GLib.SList return a modified pointer, but the value in the hash table isn't modified. It's a bit hard to explain why that is a problem, and why GLib works that way, without diving down into the C. You have a few choices here.
Use one of the containers in libgee which support multiple values with the same key, like Gee.MultiMap. If you're working on something in the Vala compiler (which I'm guessing you are, as you're subclassing CodeVisitor), this isn't an option because the internal copy of gee Vala ships with doesn't include MultiMap.
Replace the GLib.List instances in the hash table. Unfortunately this is likely going to mean copying the whole list every time, and even then getting the memory management right would be a bit tricky, so I would avoid it if I were you.
Use something other than GLib.List. This is the way I would go if I were you.
Edit: I recently added GLib.GenericSet to Vala as an alternative binding for GHashTable, so the best solution now would be to use GLib.HashTable<string, GLib.GenericSet<string>>, assuming you're okay with depending on vala >= 0.26.
If I were you, I would use GLib.HashTable<string, GLib.HashTable<unowned string, string>>:
private static int main (string[] args) {
GLib.HashTable<string, GLib.HashTable<unowned string, string>> generic_classes =
new GLib.HashTable<string, GLib.HashTable<unowned string, string>> (GLib.str_hash, GLib.str_equal);
for (int i = 0 ; i < 3 ; i++) {
string c = "foo";
string s = i.to_string ();
unowned GLib.HashTable<unowned string, string>? inner_set = generic_classes[c];
stdout.printf ("Inserting <%s, %s>, ", c, s);
if (inner_set == null) {
var v = new GLib.HashTable<unowned string, string> (GLib.str_hash, GLib.str_equal);
inner_set = v;
generic_classes.insert ((owned) c, (owned) v);
}
inner_set.insert (s, (owned) s);
stdout.printf ("container now holds:\n");
generic_classes.foreach ((k, v) => {
stdout.printf ("\t%s:\n", k);
v.foreach ((ik, iv) => {
stdout.printf ("\t\t%s\n", iv);
});
});
}
return 0;
}
It may seem hackish to have a hash table with the key and value having the same value, but this is actually a common pattern in C as well, and specifically supported by GLib's hash table implementation.
Moral of the story: don't use GLib.List or GLib.SList unless you really know what you're doing, and even then it's generally best to avoid them. TBH we probably would have marked them as deprecated in Vala long ago if it weren't for the fact that they're very common when working with C APIs.
Vala's new can be a little weird when used as a parameter. I would recommend assigning the new list to a temporary, adding it to the list, then letting it go out of scope.
I would also recommend using libgee. It has better handling of generics than GLib.List and GLib.HashTable.
in my application I have 2 layers. The first layer is a C legacy exposing cdecl functions that use the "..." syntax for a varying parameter list. The only way I found to call these functions from my .Net layer (the second one) is using the DllImport technics. For example the C function below:
int myFunc(char* name, ...);
looks like that in C#:
[DllImport("MyDll.dll"),
CharSet = CharSet.Ansi,
CallingConvention = CallingConvention.Cdecl]
int myFunc(string name, __arglist);
My problem is that sometimes I want to call this function with 2 extra parameters but if one of them is NULL it won't be included in the argument list (my legacy code fails on NULL values). For example I want this call:
int foo(string name, string a, string b)
{
myFunc(name, __arglist(a, b));
}
{
foo("john", "arg1", null);
}
to be interpreted by C as
myFunc("john", "arg1");
Unfortunately doing something like that:
int foo(string name, string a, string b)
{
List<string> args = new List<string>();
if(a != null) args.Add(a);
if(b != null) args.Add(b);
myFunc(name, __arglist(args));
}
{
foo("john", "arg1", null);
}
is interpreted by C like that:
myFunc(name, args);
and not:
myFunc(name, args[0]);
Does anybody have any idea?
How does the C function know which one is the last parameter? It cannot know a priori how many parameters there are. It needs additional information. One common way for functions get the information they need is by parsing the included string parameter to count format specifiers, as in printf. In that case, if the format string only indicates that there is one extra parameter, then the function doesn't know the difference between a call that really had just one extra parameter and a call that had two or a call that had 20. The function should have the self-discipline to only read one parameter, since that's all the format string said there was. Reading more would lead to undefined behavior.
If what I've described is not the way your function works, then there's not much you can do on the calling end to solve it. But if it is how your function works, then there's nothing to do on the calling end, because there's no problem.
Another option, since you indicate that your "legacy code fails on null values," is to fix your legacy code so it doesn't fail anymore.
A third option is to simply write all four possibilities:
if (a != null) {
if (b != null)
return myFunc(name, a, b);
else
return myFunc(name, a);
} else {
if (b != null)
return myFunc(names, b);
else
return myFunc(names);
}
More than two optional parameters, though, and the code starts getting unwieldy.
Try converting your System.List ToArray() before wrapping it in __arglist
myFunc(name, __arglist(args.ToArray()));