Auflisten oder Zuordnen einer Liste mit Index und Wert in Dart

95

Im Dart gibt es ein Äquivalent zum Common:

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

Es scheint, dass dies der einfachste Weg ist, aber es scheint immer noch seltsam, dass diese Funktionalität nicht existieren würde.

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

Bearbeiten:

Dank @ hemanth-raj konnte ich die gesuchte Lösung finden. Ich werde dies hier für jeden einfügen, der etwas Ähnliches tun muss.

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

Alternativ können Sie eine Synchrongeneratorfunktion erstellen, um eine iterable zurückzugeben

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}"));
David Rees
quelle
Map#forEach? ist es was du willst?
Pskink
Es zählt durch eine Liste keine Karte auf
David Rees
Map # forEach zählt durch ein List? Was meinst du? In den Dokumenten heißt es: "Wendet f auf jedes Schlüssel / Wert-Paar der Karte an. Durch Aufrufen von f dürfen keine Schlüssel zur Karte hinzugefügt oder daraus entfernt werden."
Pskink
Ich verstehe auch nicht, was Sie mit "Aufzählen oder Zuordnen einer Liste mit Index und Wert" meinen
Günter Zöchbauer

Antworten:

145

Es gibt eine asMapMethode, die die Liste in eine Karte konvertiert, wobei die Schlüssel der Index und die Werte das Element am Index sind. Bitte schauen Sie sich die Dokumente hier an .

Beispiel:

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

Hoffe das hilft!

Hemanth Raj
quelle
34

Es gibt keine integrierte Funktion zum Abrufen des Iterationsindex.

Wenn Sie wie ich die Idee nicht mögen, eine Map(die Datenstruktur) nur für einen einfachen Index zu erstellen , möchten Sie wahrscheinlich eine map(die Funktion), die Ihnen den Index gibt. Nennen wir es mapIndexed(wie in Kotlin):

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

Die Implementierung von mapIndexedist einfach:

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;
  }
}
Vivien
quelle
1
Dies ist eine gute Antwort, aber wahrscheinlich besser als Synchrongenerator geschrieben
David Rees
2
@ DavidRees Danke für den Vorschlag! Ich habe auch die Funktion umbenanntmapIndexed
Vivien
Es ist eine Schande, dass Dart keine einfache Möglichkeit hat, dies zu tun. Sogar 30 Jahre alte Python können dies leicht tun!
Osamu
Es stellt sich heraus, dass dies .asMap().forEach()im Wesentlichen dasselbe ist - siehe meine Antwort.
Timmmm
1
@Timmmm Ich denke asMap(), das Abrufen von Daten kostet eine zusätzliche Schleife, daher ist es nicht so effizient wie mapIndexed()oben.
Anticafe
17

Aufbauend auf der Antwort von @Hemanth Raj.

Um es zurück zu konvertieren, könnten Sie tun

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

Oder wenn Sie den Index für eine Zuordnungsfunktion benötigen, können Sie dies tun:

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

Ab Dart 2.7 können Sie extensiondie Funktionen erweitern, Iterableanstatt Hilfsmethoden schreiben zu müssen

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

Verwendung:

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
NearHuscarl
quelle
1
Dies ist die beste Antwort. Dies funktioniert sogar mit Flutter-Elementen, bei denen Sie Widgets zurückgeben können, nicht nur String.
STAHL
7

Das weitere Paket von Lukas Renggli enthält viele nützliche Tools, darunter "indiziert", die genau das tun, was Sie wollen. Aus den Dokumenten:

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

(Sie können das Offset-Argument ignorieren, es sei denn, Sie haben einen Smalltalk-Hintergrund :-).

Michael Davies
quelle
6

Anfangs dachte ich, es ['one', 'two', 'three'].asMap().forEach((index, value) { ... });wäre wirklich ineffizient, weil es so aussieht, als würde die Liste in eine Karte konvertiert. Eigentlich nicht - die Dokumentation besagt, dass eine unveränderliche Ansicht der Liste erstellt wird. Ich habe das dart2jsmit diesem Code überprüft :

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

Es generiert viel Code! Aber das Wesentliche ist:

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

Es ist also klug genug, um das Effiziente zu tun! Ziemlich clever.

Natürlich können Sie auch einfach eine normale for-Schleife verwenden:

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

Der Einfachheit halber können Sie diese Erweiterungsmethode verwenden.

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
quelle
2

Verwenden Sie asMap , um List zuerst in Map zu konvertieren. Der Index des Elements ist der Schlüssel. Das Element wird zum Wert. Verwenden Sie Einträge , um den Schlüssel und den Wert nach Belieben zuzuordnen.

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

Ausgabe:

[0:a, 1:b, 2:c]
Gary Lee
quelle