Wylicz lub zmapuj listę z indeksem i wartością w Dart


94

W dart jest jakikolwiek odpowiednik wspólnego:

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

Wydaje się, że to najłatwiejszy sposób, ale nadal wydaje się dziwne, że taka funkcjonalność nie istniała.

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

Edytować:

Dzięki @ hemanth-raj udało mi się znaleźć rozwiązanie, którego szukałem. Umieszczę to tutaj dla każdego, kto musi zrobić coś podobnego.

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

Alternatywnie możesz utworzyć synchroniczną funkcję generatora, aby zwrócić iterowalną

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? czy tego chcesz
pskink

To wyliczanie poprzez List, a nie mapę
David Rees,

Mapa # forEach wylicza za pomocą List? co masz na myśli? Dokumentacja mówi: „Stosuje f do każdej pary klucz / wartość mapy. Wywołanie f nie może dodawać ani usuwać kluczy z mapy”.
pskink

Ja też nie rozumiem co masz na myśli z „enumerate lub map przez liście z indeksem i wartości”
Günter Zochbauer

Odpowiedzi:


143

Istnieje asMapmetoda, która konwertuje listę na mapę, w której klucze są indeksem, a wartości są elementem w indeksie. Proszę spojrzeć na dokumenty tutaj .

Przykład:

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

Mam nadzieję że to pomoże!


34

Nie ma wbudowanej funkcji do uzyskania indeksu iteracji.

Jeśli tak jak ja nie podoba ci się pomysł zbudowania Map(struktury danych) tylko dla prostego indeksu, prawdopodobnie potrzebujesz map(funkcji), która daje ci indeks. Nazwijmy to mapIndexed(jak w Kotlinie):

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

Wdrożenie mapIndexedjest proste:

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
To dobra odpowiedź, ale prawdopodobnie lepiej by ją napisać jako generator synchroniczny
David Rees

2
@DavidRees thx za sugestię! Zmieniłem również nazwę funkcjimapIndexed
Vivien

Szkoda, że ​​dart nie ma wbudowanego łatwego sposobu na zrobienie tego. Nawet 30-letni Python może to łatwo zrobić!
osamu

Okazuje się, że w .asMap().forEach()zasadzie jest to to samo - zobacz moją odpowiedź.
Timmmm

1
@Timmmm Myślę, że asMap()będzie kosztować dodatkową pętlę do pobierania danych, więc nie będzie wydajna jak mapIndexed()powyżej.
anticafe

17

Opierając się na odpowiedzi @Hemanth Raj.

Aby go przekonwertować, możesz zrobić

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

Lub jeśli potrzebujesz indeksu do funkcji mapowania, możesz to zrobić:

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

14

Począwszy od Dart 2.7, możesz extensionrozszerzyć funkcjonalność Iterablezamiast pisać metody pomocnicze

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

Stosowanie:

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
to najlepsza odpowiedź, działa to nawet z elementami Flutter, w których można zwrócić Widgety, a nie tylko ciąg.
STAL

7

Więcej w pakiecie Lukasa Renggliego zawiera wiele przydatnych narzędzi, w tym „indeksowane”, które robi dokładnie to, co chcesz. Z dokumentów:

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

(Możesz zignorować argument przesunięcia, chyba że masz tło Smalltalk :-).


6

Początkowo myślałem ['one', 'two', 'three'].asMap().forEach((index, value) { ... });, że byłoby to naprawdę nieefektywne, ponieważ wygląda na to, że konwertuje listę na mapę. W rzeczywistości tak nie jest - dokumentacja mówi, że tworzy niezmienny widok listy. Dokładnie sprawdziłem dart2jsten kod:

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

Generuje dużo kodu! Ale istota jest taka:

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

Jest więc wystarczająco inteligentny, aby robić rzeczy wydajne! Całkiem sprytnie.

Oczywiście możesz też użyć zwykłej pętli for:

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

4

Dla wygody możesz skorzystać z tej metody rozszerzenia.

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

Użyj asMap, aby najpierw przekonwertować List do mapy. Indeks elementu jest kluczem. Element staje się wartością. Użyj wpisów, aby zmapować klucz i wartość na cokolwiek chcesz.

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

Wynik:

[0:a, 1:b, 2:c]
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.