Énumérer ou mapper via une liste avec index et valeur dans Dart


94

En fléchette il y a tout équivalent au commun:

enumerate(List) -> Iterator((index, value) => f)
or 
List.enumerate()  -> Iterator((index, value) => f)
or 
List.map() -> Iterator((index, value) => f)

Il semble que ce soit le moyen le plus simple, mais il semble toujours étrange que cette fonctionnalité n'existerait pas.

Iterable<int>.generate(list.length).forEach( (index) => {
  newList.add(list[index], index)
});

Éditer:

Grâce à @ hemanth-raj, j'ai pu trouver la solution que je recherchais. Je vais mettre ceci ici pour tous ceux qui ont besoin de faire quelque chose de similaire.

List<Widget> _buildWidgets(List<Object> list) {
    return list
        .asMap()
        .map((index, value) =>
            MapEntry(index, _buildWidget(index, value)))
        .values
        .toList();
}

Vous pouvez également créer une fonction de générateur synchrone pour renvoyer un itérable

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

//and use it like this.
var list = enumerate([0,1,3]).map((entry) => Text("index: ${entry.key}, value: ${entry.value}"));

Map#forEach? c'est ce que tu veux?
pskink

Il énumère à travers une liste pas une carte
David Rees

La carte # forEach est énumérée via un List? Que voulez-vous dire? les documents disent: "Applique f à chaque paire clé / valeur de la carte. L'appel de f ne doit pas ajouter ou supprimer des clés de la carte."
pskink

Je ne comprends pas non plus ce que vous entendez par "énumérer ou mapper via une liste avec index et valeur"
Günter Zöchbauer

Réponses:


143

Il existe une asMapméthode qui convertit la liste en une carte où les clés sont l'index et les valeurs sont l'élément à l'index. Veuillez consulter la documentation ici .

Exemple:

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

J'espère que cela t'aides!


34

Il n'y a pas de fonction intégrée pour obtenir l'index d'itération.

Si comme moi vous n'aimez pas l'idée de construire une Map(la structure de données) juste pour un simple index, ce que vous voulez probablement est une map(la fonction) qui vous donne l'index. Appelons-le mapIndexed(comme dans Kotlin):

children: mapIndexed(
  list,
  (index, item) => Text("event_$index")
).toList();

La mise en œuvre de mapIndexedest 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;
  }
}

1
C'est une bonne réponse mais serait probablement mieux écrite comme un générateur synchrone
David Rees

2
@DavidRees merci pour la suggestion! J'ai aussi renommé la fonctionmapIndexed
Vivien

C'est dommage que dart n'ait pas de moyen facile intégré de le faire. Même un python âgé de 30 ans peut le faire facilement!
osamu

Il s'avère que .asMap().forEach()c'est essentiellement la même chose - voir ma réponse.
Timmmm

1
@Timmmm Je pense que asMap()cela coûtera une boucle supplémentaire pour récupérer les données, donc cela ne sera pas efficace comme mapIndexed()ci-dessus.
anticafe le

17

S'appuyant sur la réponse @Hemanth Raj.

Pour le reconvertir, vous pouvez le faire

List<String> _sample = ['a', 'b', 'c'];
_sample.asMap().values.toList(); 
//returns ['a', 'b', 'c'];

Ou si vous avez besoin de l'index pour une fonction de mappage, vous pouvez le faire:

_sample
.asMap()
.map((index, str) => MapEntry(index, str + index.toString()))
.values
.toList();
// returns ['a0', 'b1', 'c2']

14

À partir de Dart 2.7, vous pouvez utiliser extensionpour étendre les fonctionnalités de Iterableau lieu d'avoir à écrire des méthodes d'assistance

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

1
c'est la meilleure réponse, cela fonctionne même avec les éléments Flutter où vous pouvez renvoyer des widgets pas seulement des chaînes.
STEEL

7

Le package plus de Lukas Renggli comprend de nombreux outils utiles, y compris «indexé» qui fait exactement ce que vous voulez. À partir de la documentation:

indexed(['a', 'b'], offset: 1)
  .map((each) => '${each.index}: ${each.value}')
  .join(', ');

(Vous pouvez ignorer l'argument offset sauf si vous avez un arrière-plan Smalltalk :-).


6

J'ai d'abord pensé que ce ['one', 'two', 'three'].asMap().forEach((index, value) { ... });serait vraiment inefficace car il semble que cela convertisse la liste en carte. En fait, ce n'est pas le cas - la documentation dit que cela crée une vue immuable de la liste. J'ai vérifié avec le dart2jsde ce code:

void main() {
  final foo = ['one', 'two', 'three'];
  foo.asMap().forEach((idx, val) {
    print('$idx: $val');
  });
}

Cela génère beaucoup de code! Mais l'essentiel est ceci:

  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
  };

Il est donc assez intelligent pour faire la chose efficace! Assez intelligent.

Bien sûr, vous pouvez également utiliser une boucle for normale:

for (var index = 0; index < values.length; ++index) {
  final value = values[index];

4

Pour plus de commodité, vous pouvez utiliser cette méthode d'extension.

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++;
    }
  }
}

2

Utilisez asMap pour convertir la liste en carte en premier. L'index d'élément est la clé. L'élément devient valeur. Utilisez des entrées pour mapper la clé et la valeur à tout ce que vous voulez.

List rawList = ["a", "b", "c"];
List<String> argList = rawList.asMap().entries.map((e) => '${e.key}:${e.value}').toList();
print(argList);

Production:

[0:a, 1:b, 2:c]
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.