Is there a way to map a Map to a List in Dart? - dart

I want to turn a map into a list of objects that contain information from the key and value of each map entry.
I know it is possible to do it this way:
myMap.map<String,Widget>((key, value) {
return MapEntry<String, Widget>(key,Widget(
//Some widget that uses key and value
));
}).values.toList();
However, I am wondering if there is a simpler way to do it without turning it into a map first. Something like:
myMap.map<Widget>((key, value) {
return Widget(
//Some widget that uses key and value
);
});
However this returns an error:
"Error: Expected 2 type arguments."

If I understand the question correct, you can map .entries instead, like this:
myMap.entries.map((e) => Widget( /* use e.key and e.value */ )).toList()

Related

Dart accessing class fields like map values

I was wondering if it was possible to access the values of fields in an object with their names in a manner analogous to accessing values in a map with the key names. For example, something like this
void main() {
MyData d=MyData();
List<String> fieldNames=['a','b','c'];
for(var name in fieldNames){
print('This is the value of the $name field: ${d[name]}}');
}
}
class MyData{
String a='A';
String b='B';
String c='C';
}
Of course, this doesn't work because Dart doesn't quite know what to make of d[name] because d is an object. But if d was a map, it would work. Like this.
void main() {
Map d=myData;
List<String> fieldNames=['a','b','c'];
for(var name in fieldNames){
print('This is the value of the $name field: ${d[name]}}');
}
}
Map myData={
'a':'A',
'b':'B',
'c':'C',
};
So my question is this. If I have a class, is there any way to treat it like a Map in the situations where I might want to refer to several of the field values indirectly via their names like I tried to do above? Or is this sort of trick not possible in a compiled language like Dart?
The short answer is "no". The longer answer is "noooooooo". :)
But seriously, the namespace of your program code is very separate from the data values that are managed by your code. This isn't JavaScript, where we can freely flow from code to data to code again.

Dart Generic Function with Subtype function call

I am not sure if this is even possible but here's my setup:
I have basically 2 Maps holding a special identifier to get some objects.
these identifier is like a versioning number, i may have data in version 8 that belongs to meta version 5. But at the same time, Meta versions up to 10 may exist and not every meta version holds information about every data, so here's where the _filter kicks in.
The filter is able to find to any given value the correct object. So far so good.
My question belongs to the following: (last codeline)
how am i able to say "if you have no matching candidate, generate me a default value"
For this purpose, i tried to force a named constructor with a super class for "Data" and "Meta" called "BasicInformation".
But even if i implement this, how do i call something like T.namedConstructor(); ?
class Repo{
Map<int, Data> mapData;
Map<int, Meta> mapMeta;
Data getData(int value)
{
return _filter<Data>(mapData, value);
}
Meta getMeta(int value)
{
return _filter<Data>(mapMeta, value);
}
T _filter<T extends BasicInformation>(Map<int, T>, int value)
{
//fancy filtering technique
//....
//speudo code
if (found) return map[found]; //speudo code
else return T.generateDefault();
}
}
I've found the following stackoverflow entry: Calling method on generic type Dart
which says, this is not possible without adding a function call.

Dart: How to check if List of Objects is a List of Strings/ints/bools

For simple data types, you can use e.g.
object is String
to check whether an Object variable is of a more specific type.
But let's you have a List, but want to check if it is a List of Strings. Intuitively we might try
List list = ['string', 'other string'];
print(list is List<String>);
which returns false.
Similarly using the List.cast() method doesn't help, it will always succeed and only throw an error later on when using the list.
We could iterate over the entire list and check the type of each individual entry, but I was hoping there might be a better way.
There is no other way. What you have is a List<Object?>/List<dynamic> (because the type is inferred from the variable type, which is a raw List type which gets instantiated to its bound). The list currently only contains String objects, but nothing prevents you from adding a new Object() to it.
So, the object itself doesn't know that it only contains strings, you have to look at each element to check that.
Or, when you create a list, just declare the variable as List<String> list = ...; or var list = ...;, then the object will be a List<String>.
If you are not the one creating the list, it's back to list.every((e) => e is String).
Each element of a List may be of any type BUT IF YOU ARE SURE that all elements have the same type this approach may be useful
bool listElementIs<T>(List l) {
if (l.isEmpty) return true;
try {
if (l[0] is T) return true; // only try to access to check element
} catch (e) {
return false;
}
return false;
}
void main() {
List list = ['string', 'other string'];
print(listElementIs<String>(list)); // prints 'true'
print(listElementIs<int>(list)); // prints 'false'
}
I think a better way to do this is to be specific about your data types.
By specifying the types of variables, you can catch potential errors early on during the development process.
In addition, specifying the types of variables makes the code more readable and understandable.
Furthermore, specifying types can also improve the performance of your code, as the compiler can make certain optimizations based on the types of variables.
List<String> list = <String>['string', 'other string'];
print(list is List<String>); /// prints true.
You can use this linter rule to enforce it:
https://dart-lang.github.io/linter/lints/always_specify_types.html

Dart passing types to functions fails

I have a map consisting of different types and strings:
const Map<Type, String> hiveTableNames = {
BreakTimeDto: "breaktime",
WorkTimeDto: "worktime"
};
And I want to loop through it because I want to call a function for each type which takes a type parameter:
Future<void> sendAll<T>(List item) async {
...
}
My attempt was to use the forEach-loop:
hiveTableNames.forEach((key, value) async {
final box = await Hive.openBox(value);
_helper.sendAll<key>(box.values.cast<key>().toList());
});
But the App throws an error: Error: 'key' isn*t a type.
Why is that? I declared the map to store types and from my understanding i pass these types in the function.
Dart separates actual types and objects of type Type. The latter are not types, and cannot be used as types, they're more like mirrors of types. A Type object can only really be used for two things: as tokens to use with dart:mirrors and comparing for equality (which isn't particularly useful except for very simple types).
The only things that can be used as type arguments to generic functions or classes are actual literal types or other type variables.
In your case, you have a Type object and wants to use the corresponding type as a type argument. That won't work, there is no way to go from a Type object to a real type.
That's a deliberate choice, it means that the compiler can see that if a type is never used as a type argument in the source code, then it will never be the type bound to a type parameter, so if you have foo<T>(T value) => ... then you know that T will never be Bar if Bar doesn't occur as a type argument, something<Bar>(), anywhere in the program.
In your case, what you can do is to keep the type around as a type by using a more complicated key object.
Perhaps:
class MyType<T> {
const MyType();
R use<R>(R Function<X>() action) => action<T>();
int get hashCode => T.hashCode;
bool operator==(Object other) => other is MyType && other.use(<S>() => T == S);
}
This allows you to store the type as a type:
final Map<MyType, String> hiveTableNames = {
const MyType<BreakTimeDto>(): "breaktime",
const MyType<WorkTimeDto>(): "worktime"
};
(I'm not making the map const because const maps must not have keys which override operator==).
Then you can use it as:
hiveTableNames.forEach((key, value) async {
final box = await Hive.openBox(value);
key.use(<K>() =>
_helper.sendAll<K>([for (var v in box.values) v as K]);
}
(If all you are using your map for is iterating the key/value pairs, then it's really just a list of pairs, not a map, so I assume you are using it for lookups, which is why MyType override operator==).
In general, you should avoid using Type objects for anything, they're very rarely the right tool for any job.

Java 8 stream collector breaks HashMap insertion semantics?

So I'm trying to use the Java 8 streams Collectors.toMap to add elements to a newly created Map.
The Map to be created may contain null values for some of its keys. This is perfectly acceptable for a HashMap, and when I use a stream forEach to add member it works as expected:
final Map<String, Method> rMap = new HashMap<> ( );
msgRouteProps.entrySet ( )
.stream ( )
.forEach (entry -> rMap.put (entry.getKey ( ),
ReflectionUtil.getNamedMethod (processor, entry.getValue ( ))));
The msgRouteProps is a Map where the keys and values are all non-null. Note that the ReflectionUtil.getNamedMethod () call may return a null, which I want to be put into the resulting Map.
So I wanted to explore how to do the same thing with the Collectors.toMap() method, so the stream operation could return the newly created map, without me having to create it directly.
So I tried the following:
Map<String, Method> rMap =
msgRouteProps.entrySet ( )
.stream ( )
.collect (Collectors.toMap (Map.Entry::getKey,
(e) -> ReflectionUtil.getNamedMethod (processor, e.getValue ( ))));
It seems to me that this should work the same way, but unfortunately, it fails with a null pointer exception. It appears that the Collectors.toMap method calls the HashMap's merge() method which doesn't allow null values to be passed. What seems odd to me is that I don't really think that merge should be getting called at all in this case, since the keys in the original map are all unique. Given that, I would assume that the values could simply be added to the new map via calling the map's put() method, which will allow null values.
Is there something that I'm doing wrong here, or is there a work-around for this? Or is this simply the expected behavior when using Collectors.toMap?
It's a known bug already been reported by many users
The best way is to use forEach if you have null values. Still if you want to use collect then try the following work around
Map<String, Method> rMap =
msgRouteProps.entrySet()
.stream()
.collect(HashMap::new, (m,v) -> m.put(v.getKey(), ReflectionUtil.getNamedMethod(processor, v.getValue())), HashMap::putAll);
Also take a look at this answer as well

Resources