Enumerate or map through a list with index and value in Dart
DartFlutterDart Problem Overview
In dart there any equivalent to the common:
enumerate(List) -> Iterator((index, value) => f)
or
List.enumerate() -> Iterator((index, value) => f)
or
List.map() -> Iterator((index, value) => f)
It seems that this is the easiest way but it still seems strange that this functionality wouldn't exist.
Iterable<int>.generate(list.length).forEach( (index) => {
newList.add(list[index], index)
});
Dart Solutions
Solution 1 - Dart
There is a asMap
method which converts the list to a map where the keys are the index and values are the element at index. Please take a look at the docs here.
Example:
List _sample = ['a','b','c'];
_sample.asMap().forEach((index, value) => f);
Hope this helps!
Solution 2 - Dart
You can use the mapIndexed
or forEachIndexed
extension methods from the collection
package. Note that unlike javascript's array.map()
or C#'s IEnumerable.Select()
, the index is the first argument, not the second argument of the callback:
import 'package:collection/collection.dart';
void main() {
final inputs = ['a', 'b', 'c', 'd', 'e', 'f'];
final indexes = inputs.mapIndexed((index, element) => index).toList();
inputs.forEachIndexed((index, element) {
print('index: $index, element: $element');
});
print(indexes);
}
Live Demo
Old answer
Starting with Dart 2.7, you can use extension
methods to extend the functionalities of Iterable
instead of having to write helper functions:
extension ExtendedIterable<E> on Iterable<E> {
/// Like Iterable<T>.map but the callback has index as second argument
Iterable<T> mapIndexed<T>(T Function(E e, int i) f) {
var i = 0;
return map((e) => f(e, i++));
}
void forEachIndexed(void Function(E e, int i) f) {
var i = 0;
forEach((e) => f(e, i++));
}
}
Usage:
final inputs = ['a', 'b', 'c', 'd', 'e', 'f'];
final results = inputs
.mapIndexed((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.forEachIndexed((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
Live Demo
Solution 3 - Dart
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;
}
}
Solution 4 - Dart
Building on @Hemanth Raj answer.
To convert it back you could do
List<String> _sample = ['a', 'b', 'c'];
_sample.asMap().values.toList();
//returns ['a', 'b', 'c'];
Or if you needed the index for a mapping function you could do this:
_sample
.asMap()
.map((index, str) => MapEntry(index, str + index.toString()))
.values
.toList();
// returns ['a0', 'b1', 'c2']
Solution 5 - Dart
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]
Solution 6 - Dart
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];
Solution 7 - Dart
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 :-).
Solution 8 - Dart
You can use the mapIndexed
extension from the collections package:
import 'package:collection/collection.dart';
void main() {
final nums = [1, 2, 3];
final strs = nums.mapIndexed((index, element) => index.toString() + '_' + element.toString()).toList();
print(strs); // [0_1, 1_2, 2_3]
}
Solution 9 - Dart
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++;
}
}
}
Solution 10 - Dart
use -> mapIndexed
to map each element and its index to a new value.
import 'package:collection/collection.dart';
and use the map index as follows
(List).mapIndexed<Widget>(
(mapIndex, mapElement) => Positioned(
left: mapIndex.toDouble() * 5,
child: Card(
color: Colors.blue,
child: Image.network(
'${mapElement.ImageURL}',
height: 80,
width: 80))))
Please Refer: https://pub.dev/documentation/collection/latest/collection/IterableExtension/mapIndexed.html
Solution 11 - Dart
You can use Iterable.generate
factory. The following code would map an Iterable
using indexes and values.
extension IterableMapIndex<T> on Iterable<T> {
Iterable<E> mapIndexed<E>(E f(int index, T t)) {
return Iterable.generate(this.length, (index)=>f(index, elementAt(index)));
}
}
Solution 12 - Dart
Using dart collection package you can access various list extensions
one is mapIndexed
:
Iterable<R> mapIndexed<R>(R Function(int, E) convert)
Solution 13 - Dart
You can create another variable to get index.
int index = 0;
array?.forEach((element) {
// your code is here
index++; // you should add this at end of the for loop
});
Solution 14 - Dart
package:quiver
includes an enumerate
function that is similar to Python's enumerate
function.
(Note that although package:quiver
isn't from the Dart team, it is owned and maintained by Google, and many of its contributors are members of Google's Dart and Flutter teams.)