Home > Software design >  How do I get a nested element in a List or a Map with a provided List in Dart?
How do I get a nested element in a List or a Map with a provided List in Dart?

Time:01-04

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

CodePudding user response:

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.

  •  Tags:  
  • Related