Enumerar o mapear a través de una lista con índice y valor en Dart


94

En dardo hay cualquier equivalente al común:

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

Parece que esta es la forma más fácil, pero todavía parece extraño que esta funcionalidad no exista.

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

Editar:

Gracias a @ hemanth-raj pude encontrar la solución que estaba buscando. Lo pondré aquí para cualquiera que necesite hacer algo similar.

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

Alternativamente, puede crear una función de generador síncrono para devolver un iterable

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? ¿Esto es lo que quieres?
pskink

Se enumera a través de una lista, no un mapa
David Rees

Map # forEach está enumerando a través de un List? ¿Qué quieres decir? los documentos dicen: "Aplica f a cada par clave / valor del mapa. Llamar a f no debe agregar ni quitar claves del mapa".
pskink

Tampoco entiendo lo que quiere decir con "enumerar o mapear a través de una lista con índice y valor"
Günter Zöchbauer

Respuestas:


143

Existe un asMapmétodo que convierte la lista en un mapa donde las claves son el índice y los valores son el elemento en el índice. Por favor, eche un vistazo a los documentos aquí .

Ejemplo:

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

¡Espero que esto ayude!


34

No hay una función incorporada para obtener el índice de iteración.

Si, como a mí, no le gusta la idea de construir un Map(la estructura de datos) solo para un índice simple, lo que probablemente quiera es un map(la función) que le da el índice. Llamémoslo mapIndexed(como en Kotlin):

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

La implementación de mapIndexedes 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
Esta es una buena respuesta, pero probablemente estaría mejor escrita como un generador síncrono
David Rees

2
@DavidRees ¡gracias por la sugerencia! También cambié el nombre de la funciónmapIndexed
Vivien

Es una pena que Dart no tenga una forma fácil incorporada de hacer esto. ¡Incluso una pitón de 30 años puede hacer esto fácilmente!
osamu

Resulta que .asMap().forEach()es esencialmente lo mismo que esto: vea mi respuesta.
Timmmm

1
@Timmmm Creo asMap()que costará un bucle adicional para recuperar datos, por lo que no será eficiente como el mapIndexed()anterior.
Anticafe

17

Sobre la base de la respuesta de @Hemanth Raj.

Para convertirlo de nuevo, podrías hacer

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

O si necesita el índice para una función de mapeo, puede hacer esto:

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

14

A partir de Dart 2.7, puede utilizar extensionpara ampliar las funcionalidades en Iterablelugar de tener que escribir métodos auxiliares

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

Uso:

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
esta es la mejor respuesta, esto incluso funciona con elementos de Flutter donde podría devolver Widgets no solo String.
ACERO

7

El paquete más de Lukas Renggli incluye muchas herramientas útiles, incluido "indexado", que hace exactamente lo que quieres. De los documentos:

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

(Puede ignorar el argumento de compensación a menos que tenga antecedentes en Smalltalk :-).


6

Inicialmente pensé ['one', 'two', 'three'].asMap().forEach((index, value) { ... });que sería realmente ineficiente porque parece que está convirtiendo la lista en un mapa. En realidad no lo es, la documentación dice que crea una vista inmutable de la lista. Verifiqué dos veces con el dart2jsde este código:

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

¡Genera mucho código! Pero la esencia es esta:

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

¡Así que es lo suficientemente inteligente como para hacer lo eficiente! Muy inteligente.

Por supuesto, también puede usar un bucle for normal:

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

4

Para mayor comodidad, puede utilizar este método de extensión.

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

Utilice asMap para convertir la lista en mapa primero. El índice de elemento es la clave. El elemento se convierte en valor. Utilice entradas para asignar la clave y el valor a lo que desee.

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

Salida:

[0:a, 1:b, 2:c]
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.