In dart there any equivalent to the common:
enumerate(List) -> Iterator((index, value) => f)
or
List.enumerate() -> Iterator((index, value) =>
There is no built-in function to get the iteration index.
If like me you don't like the idea to build a Map
(the data structure) just for a simple index, what you probably want is a map
(the function) which gives you the index. Let's call it mapIndexed
(like in Kotlin):
children: mapIndexed(
list,
(index, item) => Text("event_$index")
).toList();
The implementation of mapIndexed
is simple:
Iterable<E> mapIndexed<E, T>(
Iterable<T> items, E Function(int index, T item) f) sync* {
var index = 0;
for (final item in items) {
yield f(index, item);
index = index + 1;
}
}
Use asMap to convert List to map first. The index of element is the key. The element becomes value. Use entries to map the key and value to anything you want.
List rawList = ["a", "b", "c"];
List<String> argList = rawList.asMap().entries.map((e) => '${e.key}:${e.value}').toList();
print(argList);
Output:
[0:a, 1:b, 2:c]
I initially thought ['one', 'two', 'three'].asMap().forEach((index, value) { ... });
would be really inefficient because it looks like it is converting the list to a map. Actually it isn't - the documentation says it creates an immutable view of the list. I double checked with the dart2js
of this code:
void main() {
final foo = ['one', 'two', 'three'];
foo.asMap().forEach((idx, val) {
print('$idx: $val');
});
}
It generates lot of code! But the gist is this:
main: function() {
var foo = H.setRuntimeTypeInfo(["one", "two", "three"], ...);
new H.ListMapView(foo, ...).forEach$1(0, new F.main_closure());
},
H.ListMapView.prototype = {
forEach$1: function(_, f) {
var t1, $length, t2, i;
...
t1 = this._values;
$length = t1.length;
for (t2 = $length, i = 0; i < $length; ++i) {
if (i >= t2)
return H.ioore(t1, i);
f.call$2(i, t1[i]);
t2 = t1.length;
if ($length !== t2)
throw H.wrapException(P.ConcurrentModificationError$(t1));
}
},
...
},
F.main_closure.prototype = {
call$2: function(idx, val) {
...
H.printString("" + idx + ": " + H.S(val));
},
$signature: 1
};
So it is smart enough to do the efficient thing! Pretty clever.
Of course you can also just use a normal for loop:
for (var index = 0; index < values.length; ++index) {
final value = values[index];
Starting with Dart 2.7, you can use extension
to extend the functionalities of Iterable
instead of having to write helper methods
extension ExtendedIterable<E> on Iterable<E> {
/// Like Iterable<T>.map but callback have index as second argument
Iterable<T> mapIndex<T>(T f(E e, int i)) {
var i = 0;
return this.map((e) => f(e, i++));
}
void forEachIndex(void f(E e, int i)) {
var i = 0;
this.forEach((e) => f(e, i++));
}
}
Usage:
final inputs = ['a', 'b', 'c', 'd', 'e', 'f'];
final results = inputs
.mapIndex((e, i) => 'item: $e, index: $i')
.toList()
.join('\n');
print(results);
// item: a, index: 0
// item: b, index: 1
// item: c, index: 2
// item: d, index: 3
// item: e, index: 4
// item: f, index: 5
inputs.forEachIndex((e, i) => print('item: $e, index: $i'));
// item: a, index: 0
// item: b, index: 1
// item: c, index: 2
// item: d, index: 3
// item: e, index: 4
// item: f, index: 5
For convenience you can use this extension method.
extension CollectionUtil<T> on Iterable<T> {
Iterable<E> mapIndexed<E, T>(E Function(int index, T item) transform) sync* {
var index = 0;
for (final item in this) {
yield transform(index, item as T);
index++;
}
}
}
Lukas Renggli's more package includes many useful tools including 'indexed' which does exactly what you want. From the docs:
indexed(['a', 'b'], offset: 1)
.map((each) => '${each.index}: ${each.value}')
.join(', ');
(You can ignore the offset argument unless you have a Smalltalk background :-).