210

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)
});
NearHuscarl
  • 66,950
  • 18
  • 261
  • 230
David Rees
  • 6,792
  • 3
  • 31
  • 39
  • `Map#forEach`? is it what you want? – pskink Feb 27 '19 at 05:48
  • It's enumerating through a List not a Map – David Rees Feb 27 '19 at 05:52
  • [Map#forEach](https://docs.flutter.io/flutter/dart-core/Map/forEach.html) is enumerating through a `List`? what do you mean? the docs say: *"Applies f to each key/value pair of the map. Calling f must not add or remove keys from the map."* – pskink Feb 27 '19 at 05:57
  • I also do not understand what you mean with "enumerate or map through a list with index and value" – Günter Zöchbauer Feb 27 '19 at 06:34
  • 1
    @GünterZöchbauer https://github.com/dart-lang/sdk/issues/5245 and https://github.com/dart-lang/sdk/issues/32467 – Matt C May 02 '19 at 21:55

16 Answers16

317

There is an asMap method which converts the List to a Map where the keys are the indices and the values are the elements. Please take a look at the documentation for asMap.

Example:

final sample = ['a', 'b', 'c'];
sample.asMap().forEach((index, value) => f(index, value));

If you are using Dart 3, you can alternatively use the indexed property. Please take a look at the documentation for indexed.

Example:

final sample = ['a', 'b', 'c'];
for (final (index, item) in sample.indexed) {
  f(index, item);
}
Hemanth Raj
  • 32,555
  • 10
  • 92
  • 82
  • 51
    Quite disappointing for a JS dev, that there's a need for "conversion" here to iterate through a list with index – rehman_00001 Feb 22 '21 at 06:43
  • 5
    map() is supposed to return a value so that that one type of list is transformed into a new list type. forEach() returns void. You might as well only use forEach(). – user2233706 May 01 '21 at 14:37
  • @user2233706 `asMap` is different that `map`. `map` iterated through an `Iterable` and maps it to the return type of mapper function. `asMap` converts `Iterable` to a `Map`. The forEach is applied on the Map returned by `asMap` – Hemanth Raj May 04 '21 at 04:56
  • 5
    I get a feeling this doesn't guarantee ordering. Does it? – sleighty Aug 19 '21 at 19:53
  • 5
    @sleighty It guarantees ordering. As stated in [the `.asMap` documentation](https://api.dart.dev/stable/dart-core/List/asMap.html): *The `Map.keys` `Iterable` iterates the indices of this list in numerical order.* – jamesdlin Mar 24 '22 at 05:35
  • 3
    One disadvantage of `.asMap` is that it works only on `List`s and not on arbitrary `Iterable`s. – jamesdlin Mar 24 '22 at 05:36
  • Because `Map.map()` has the signature `map(MapEntry Function(K, V))`, it is impossible to convert a list to another data structure via e.g. `someList.asMap().map().toList()`. – ynn May 13 '23 at 06:34
191

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

NearHuscarl
  • 66,950
  • 18
  • 261
  • 230
  • 20
    this is the best answer, this even works with Flutter items where you could return Widgets not just String. – STEEL Sep 30 '20 at 15:21
  • So, where you have `List`, T and E represent List and Widget respectively? How does the incremental line work with a return statement ie. not explicitly passing the increment via a closure. I imagined they'll all receive "1" as their index. Secondly, why doesn't `i` start at -1? – I Want Answers Feb 13 '21 at 18:19
  • 1
    @IWantAnswers `E` and `T` represents the type of the item in the original array/output array respectively, because you can map to an array with different type. For example, if you write a function that get all names from a list of `Widget`, it'd be `widgetList.mapIndex(w => getName(w))` where `widgetList` is `List` and the result is `List` – NearHuscarl Feb 13 '21 at 18:33
  • 1
    @IWantAnswers the increment happens every time after the callback `(e) => f(e, i++)` is evaluated. If `i` starts at `-1`, it'd need to be `++i`. e.g. the increment happens before the callback is evaluated. – NearHuscarl Feb 13 '21 at 18:40
  • I get the "generic" explanation. Naming things well always helps. It's interesting that i++ and ++i both represent different behaviour in .net. Your response on the closure is what I suspected but haven't quite come to terms with cuz I can't wrap my head around the Internals of such a parameter. How does the compiler maintain reference to "increment"? It is neither a type nor a fixed variable. So, how do the closures stored on the stack know to evaluate their argument during each iteration? Or maybe I'm just being pedantic and should go with the flow. Learning from your answers. Many thanks! – I Want Answers Feb 15 '21 at 06:40
  • I think I get it. `(e) => f(e, i++))` is not a function signature but an expression itself. It could as well be "$i is a number". It's a neat trick – I Want Answers Feb 15 '21 at 06:54
  • 2
    Still disappointed for this is not builtin in dart. – John Wang Jun 17 '21 at 10:53
  • 1
    @JohnWang you can use the collection package now which is developed by dart team, the best part is that it's also used by flutter so you don't have to install it, just import the package and use the `.forEachIndexed` or `.mapIndexed` variants. My answer was updated with an example for reference. – NearHuscarl Oct 30 '21 at 02:32
  • thanks for sharing this code – mamena tech Dec 17 '21 at 19:14
47

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;
  }
}
Taufiq Rahman
  • 5,600
  • 2
  • 36
  • 44
Vivien
  • 768
  • 6
  • 9
25

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']
Jørgen Andersen
  • 641
  • 9
  • 19
21

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];
Timmmm
  • 88,195
  • 71
  • 364
  • 509
18

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]
Gary Lee
  • 359
  • 3
  • 4
12

use -> mapIndexed(index, Element) Function

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

Tarish
  • 468
  • 8
  • 8
10

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 :-).

Michael Davies
  • 1,542
  • 1
  • 12
  • 19
9

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]
}
user2233706
  • 6,148
  • 5
  • 44
  • 86
  • Importantly the collection package is shipped **inside** the Flutter SDK so is now available "out of the box" (https://api.flutter.dev/flutter/package-collection_collection/package-collection_collection-library.html). – Maks Aug 13 '21 at 06:54
  • That doesn't have the extension functions, though. – user2233706 Aug 14 '21 at 00:25
  • yes it does: https://api.flutter.dev/flutter/package-collection_collection/IterableExtension.html – Maks Aug 14 '21 at 03:21
  • @Maks what they meant is that you cannot import the extension methods without having to install the `collection` package. It's not in the sdk. See [this](https://stackoverflow.com/questions/65834536/how-to-use-lowerbound-in-flutter?rq=1#comment116403034_65834536). – NearHuscarl Sep 18 '21 at 12:07
  • @NearHuscarl I'm sorry but you are incorrect, the Flutter SDK *does* ship with the collections pkg because the SDK itself makes use of it. You can easily verify this by creating a new flutter project: `flutter create use_collection` then add the above code in this answer into `main.dart` & you will see collection pkg *is* available and it works without you having to add the pkg to your pubspec. – Maks Sep 18 '21 at 23:43
  • 1
    @Maks Thanks for correcting me. I never knew that before. It all makes sense now why the dartpad has the collection package and some others out of the box. This is also briefly [mentioned](https://api.flutter.dev/index.html#:~:text=can%20use%20it.-,Supporting%20Libraries,-Libraries%20in%20other) in the docs. – NearHuscarl Sep 19 '21 at 00:27
6

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++;
    }
  }
}
user1998494
  • 614
  • 7
  • 7
5

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)));
  }
}
navid
  • 51
  • 1
  • 4
3

Using dart collection package you can access various list extensions

one is mapIndexed:

Iterable<R> mapIndexed<R>(R Function(int, E) convert)

list of all iterable extensions

Hashem Aboonajmi
  • 13,077
  • 8
  • 66
  • 75
  • 1
    Importantly the collection package is shipped **inside** the Flutter SDK so is now available "out of the box" (https://api.flutter.dev/flutter/package-collection_collection/package-collection_collection-library.html). – Maks Aug 13 '21 at 06:54
2

With records in Dart 3, a new extension property indexed is added to the IterableExtensions which can be used like this:

final iterable = [1,2,3,4];
for(var (index, value) in iterable.indexed) {
    print('$index => $value)
}
Umair M
  • 10,298
  • 6
  • 42
  • 74
0

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.)

jamesdlin
  • 81,374
  • 13
  • 159
  • 204
0

Here's a solution from 2023 that uses the new Dart 3 record type.

You can use it like this:

for (final (index, item) in enumerate(items)) {
  print('$index: $item');
}

And this is the helper function:

Iterable<(int, T)> enumerate<T>(Iterable<T> items) sync* {
  var index = 0;
  for (final item in items) {
    yield (index, item);
    index++;
  }
}

It is similar to the enumerate function in Python.

HelloThere
  • 21
  • 2
-1

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
    });
BIS Tech
  • 17,000
  • 12
  • 99
  • 148