Перечислять или отображать в списке с индексом и значением в дартс

В дротике есть какой-то эквивалент общего:

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

Кажется, что это самый простой способ, но все же кажется странным, что эта функциональность не существует.

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

Редактировать:

Благодаря @ hemanth-raj я смог найти решение, которое искал. Я положу это здесь для тех, кто должен сделать что-то подобное.

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

16 ответов

Решение

E сть asMap метод, который преобразует список в карту, где ключи - это индекс, а значения - элемент в индексе. Пожалуйста, посмотрите на документы здесь.

Пример:

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

Надеюсь это поможет!

Начиная с Dart 2.7, вы можете использовать extension для расширения функциональности Iterable вместо написания вспомогательных методов

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

Применение:

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

Нет встроенной функции для получения индекса итерации.

Если, как я, вам не нравится идея построить Map (структура данных) просто для простого индекса, что вы, вероятно, хотите, это map (функция), которая дает вам индекс. Давайте позвоним mapIndexed (как в Котлине):

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

Реализация mapIndexed это просто:

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

Опираясь на ответ @Hemanth Raj.

Чтобы преобразовать его обратно, вы можете сделать

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

Или, если вам нужен индекс для функции отображения, вы можете сделать это:

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

Я изначально думал ['one', 'two', 'three'].asMap().forEach((index, value) { ... });было бы действительно неэффективно, потому что похоже, что он преобразует список в карту. На самом деле это не так - в документации говорится, что он создает неизменяемое представление списка. Я дважды проверилdart2js этого кода:

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

Он генерирует много кода! Но суть такова:

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

Так что он достаточно умен, чтобы делать все эффективно! Довольно умно.

Конечно, вы также можете использовать обычный цикл for:

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

Используйте asMap, чтобы сначала преобразовать список в карту. Индекс элемента - это ключ. Элемент становится ценностью. Используйте записи, чтобы сопоставить ключ и значение с чем угодно.

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

Выход:

[0:a, 1:b, 2:c]

использовать -> функция mapIndexed(индекс, элемент)

чтобы сопоставить каждый элемент и его индекс с новым значением.

      import 'package:collection/collection.dart';

и используйте индекс карты следующим образом

      (List).mapIndexed<Widget>(
 (mapIndex, mapElement) => Positioned(
  left: mapIndex.toDouble() * 5,
  child: Card(
   color: Colors.blue,
    child: Image.network(
     '${mapElement.ImageURL}',
      height: 80,
      width: 80))))

Пожалуйста, обратитесь:https://pub.dev/documentation/collection/latest/collection/IterableExtension/mapIndexed.html

Пакет more от Lukas Renggli включает в себя множество полезных инструментов, включая "indexed", который делает именно то, что вы хотите. Из документов:

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

(Вы можете игнорировать аргумент смещения, если у вас нет фона Smalltalk:-).

Вы можете использовать mapIndexedрасширение из пакета коллекций :

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

Для удобства вы можете использовать этот метод расширения.

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

Ты можешь использовать Iterable.generateфабрика. Следующий код отобразит Iterable с использованием индексов и значений.

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

Используя dart пакетcollection, вы можете получить доступ к различным расширениям списков

один mapIndexed:

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

список всех повторяемых расширений

При использовании записей в Dart 3 в список добавляется новое индексированное свойство расширения.IterableExtensionsкоторый можно использовать следующим образом:

      final iterable = [1,2,3,4];
for(var (index, value) in iterable.indexed) {
    print('$index => $value)
}

Вы можете создать другую переменную для получения index.

          int index = 0;

    array?.forEach((element) {
    
    // your code is here

    index++; // you should add this at end of the for loop
    });

Вот решение 2023 года, использующее новый тип записи Dart 3 .

Вы можете использовать его следующим образом:

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

А это вспомогательная функция:

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

Это похоже наenumerateфункция в Python.

включает в себяфункция, похожая на Python enumerateфункция.

(Обратите внимание, что хотя package:quiverне из команды Dart, он принадлежит и поддерживается Google, и многие из его участников являются членами команд Google Dart и Flutter.)

Другие вопросы по тегам