support ecmascript javascript ecmascript-6

ecmascript - Transformando un iterador Javascript en una matriz



ecmascript 6 support (6)

Una pequeña actualización de 2019:

Ahora, Array.from parece estar disponible universalmente y, además, acepta un segundo argumento mapFn , que le impide crear una matriz intermedia. Esto básicamente se ve así:

Array.from(myMap.entries(), entry => {...});

Estoy tratando de usar el nuevo objeto Map de Javascript EC6, ya que ya es compatible con las últimas versiones de Firefox y Chrome.

Pero lo encuentro muy limitado en la programación "funcional", porque carece de métodos clásicos de mapa, filtro, etc. que funcionen bien con un par [key, value] . Tiene forEach pero eso NO devuelve el resultado de devolución de llamada.

Si pudiera transformar su map.entries() de un MapIterator en una matriz simple, podría usar el estándar .map , .filter sin hacks adicionales.

¿Hay una forma "buena" de transformar un iterador Javascript en una matriz? En python es tan fácil como hacer una list(iterator) ... ¡pero Array(m.entries()) devuelve una matriz con el iterador como primer elemento!

EDITAR

Olvidé especificar que estoy buscando una respuesta que funcione donde sea que funcione Map, lo que significa al menos Chrome y Firefox (Array.from no funciona en Chrome).

PD.

Sé que existe el fantástico wu.js pero su dependencia de traceur me desanima ...


Está buscando la nueva función Array.from que convierte Array.from arbitrarios en instancias de matriz:

var arr = Array.from(map.entries());

Ahora es compatible con Edge, FF, Chrome y Node 4+ .

Por supuesto, podría valer la pena definir el map , el filter y métodos similares directamente en la interfaz del iterador, para que pueda evitar la asignación de la matriz. También es posible que desee utilizar una función generadora en lugar de funciones de orden superior:

function* map(iterable) { var i = 0; for (var item of iterable) yield yourTransformation(item, i++); } function* filter(iterable) { var i = 0; for (var item of iterable) if (yourPredicate(item, i++)) yield item; }


No hay necesidad de transformar un Map en una Array . Simplemente puede crear funciones de map y filter para objetos de Map :

function map(functor, object, self) { var result = new Map; object.forEach(function (value, key, object) { result.set(key, functor.call(this, value, key, object)); }, self); return result; } function filter(predicate, object, self) { var result = new Map; object.forEach(function (value, key, object) { if (predicate.call(this, value, key, object)) result.set(key, value); }, self); return result; }

Por ejemplo, podría agregar una explosión (es decir ! carácter ! ) Al valor de cada entrada de un mapa cuya clave es primitiva.

var object = new Map; object.set("", "empty string"); object.set(0, "number zero"); object.set(object, "itself"); var result = map(appendBang, filter(primitive, object)); alert(result.get("")); // empty string! alert(result.get(0)); // number zero! alert(result.get(object)); // undefined function primitive(value, key) { return isPrimitive(key); } function appendBang(value) { return value + "!"; } function isPrimitive(value) { var type = typeof value; return value === null || type !== "object" && type !== "function"; }

<script> function map(functor, object, self) { var result = new Map; object.forEach(function (value, key, object) { result.set(key, functor.call(this, value, key, object)); }, self || null); return result; } function filter(predicate, object, self) { var result = new Map; object.forEach(function (value, key, object) { if (predicate.call(this, value, key, object)) result.set(key, value); }, self || null); return result; } </script>

También puede agregar métodos de map y filter en Map.prototype para que se lea mejor. Aunque generalmente no se recomienda modificar los prototipos nativos, creo que se puede hacer una excepción en el caso de map y filter para Map.prototype :

var object = new Map; object.set("", "empty string"); object.set(0, "number zero"); object.set(object, "itself"); var result = object.filter(primitive).map(appendBang); alert(result.get("")); // empty string! alert(result.get(0)); // number zero! alert(result.get(object)); // undefined function primitive(value, key) { return isPrimitive(key); } function appendBang(value) { return value + "!"; } function isPrimitive(value) { var type = typeof value; return value === null || type !== "object" && type !== "function"; }

<script> Map.prototype.map = function (functor, self) { var result = new Map; this.forEach(function (value, key, object) { result.set(key, functor.call(this, value, key, object)); }, self || null); return result; }; Map.prototype.filter = function (predicate, self) { var result = new Map; this.forEach(function (value, key, object) { if (predicate.call(this, value, key, object)) result.set(key, value); }, self || null); return result; }; </script>

Editar: En la respuesta de Bergi, creó un map genérico y funciones de generador de filter para todos los objetos iterables. La ventaja de usarlos es que, dado que son funciones generadoras, no asignan objetos iterables intermedios.

Por ejemplo, mis funciones de map y filter definidas anteriormente crean nuevos objetos de Map . Por lo tanto, llamar a object.filter(primitive).map(appendBang) crea dos nuevos objetos Map :

var intermediate = object.filter(primitive); var result = intermediate.map(appendBang);

Crear objetos iterables intermedios es costoso. Las funciones del generador de Bergi resuelven este problema. No asignan objetos intermedios, pero permiten que un iterador alimente sus valores perezosamente al siguiente. Este tipo de optimización se conoce como fusión o deforestación en lenguajes de programación funcionales y puede mejorar significativamente el rendimiento del programa.

El único problema que tengo con las funciones generadoras de Bergi es que no son específicas de los objetos Map . En cambio, se generalizan para todos los objetos iterables. Por lo tanto, en lugar de llamar a las funciones de devolución de llamada con pares (value, key) (como esperaría al mapear sobre un Map ), llama a las funciones de devolución de llamada con pares (value, index) . De lo contrario, es una excelente solución y definitivamente recomendaría usarla sobre las soluciones que proporcioné.

Estas son las funciones específicas del generador que usaría para mapear y filtrar objetos Map :

function * map(functor, entries, self) { var that = self || null; for (var entry of entries) { var key = entry[0]; var value = entry[1]; yield [key, functor.call(that, value, key, entries)]; } } function * filter(predicate, entries, self) { var that = self || null; for (var entry of entries) { var key = entry[0]; var value = entry[1]; if (predicate.call(that, value, key, entries)) yield [key, value]; } } function toMap(entries) { var result = new Map; for (var entry of entries) { var key = entry[0]; var value = entry[1]; result.set(key, value); } return result; } function toArray(entries) { var array = []; for (var entry of entries) { array.push(entry[1]); } return array; }

Se pueden usar de la siguiente manera:

var object = new Map; object.set("", "empty string"); object.set(0, "number zero"); object.set(object, "itself"); var result = toMap(map(appendBang, filter(primitive, object.entries()))); alert(result.get("")); // empty string! alert(result.get(0)); // number zero! alert(result.get(object)); // undefined var array = toArray(map(appendBang, filter(primitive, object.entries()))); alert(JSON.stringify(array, null, 4)); function primitive(value, key) { return isPrimitive(key); } function appendBang(value) { return value + "!"; } function isPrimitive(value) { var type = typeof value; return value === null || type !== "object" && type !== "function"; }

<script> function * map(functor, entries, self) { var that = self || null; for (var entry of entries) { var key = entry[0]; var value = entry[1]; yield [key, functor.call(that, value, key, entries)]; } } function * filter(predicate, entries, self) { var that = self || null; for (var entry of entries) { var key = entry[0]; var value = entry[1]; if (predicate.call(that, value, key, entries)) yield [key, value]; } } function toMap(entries) { var result = new Map; for (var entry of entries) { var key = entry[0]; var value = entry[1]; result.set(key, value); } return result; } function toArray(entries) { var array = []; for (var entry of entries) { array.push(entry[1]); } return array; } </script>

Si desea una interfaz más fluida, puede hacer algo como esto:

var object = new Map; object.set("", "empty string"); object.set(0, "number zero"); object.set(object, "itself"); var result = new MapEntries(object).filter(primitive).map(appendBang).toMap(); alert(result.get("")); // empty string! alert(result.get(0)); // number zero! alert(result.get(object)); // undefined var array = new MapEntries(object).filter(primitive).map(appendBang).toArray(); alert(JSON.stringify(array, null, 4)); function primitive(value, key) { return isPrimitive(key); } function appendBang(value) { return value + "!"; } function isPrimitive(value) { var type = typeof value; return value === null || type !== "object" && type !== "function"; }

<script> MapEntries.prototype = { constructor: MapEntries, map: function (functor, self) { return new MapEntries(map(functor, this.entries, self), true); }, filter: function (predicate, self) { return new MapEntries(filter(predicate, this.entries, self), true); }, toMap: function () { return toMap(this.entries); }, toArray: function () { return toArray(this.entries); } }; function MapEntries(map, entries) { this.entries = entries ? map : map.entries(); } function * map(functor, entries, self) { var that = self || null; for (var entry of entries) { var key = entry[0]; var value = entry[1]; yield [key, functor.call(that, value, key, entries)]; } } function * filter(predicate, entries, self) { var that = self || null; for (var entry of entries) { var key = entry[0]; var value = entry[1]; if (predicate.call(that, value, key, entries)) yield [key, value]; } } function toMap(entries) { var result = new Map; for (var entry of entries) { var key = entry[0]; var value = entry[1]; result.set(key, value); } return result; } function toArray(entries) { var array = []; for (var entry of entries) { array.push(entry[1]); } return array; } </script>

Espero que ayude.


Puede obtener la matriz de matrices (clave y valor):

[...this.state.selected.entries()] /** *(2) [Array(2), Array(2)] *0: (2) [2, true] *1: (2) [3, true] *length: 2 */

Y luego, puede obtener valores fácilmente desde adentro, como por ejemplo las claves con el iterador de mapa.

[...this.state.selected[asd].entries()].map(e=>e[0]) //(2) [2, 3]


Puede usar una biblioteca como https://www.npmjs.com/package/itiriri que implementa métodos de tipo matriz para iterables:

import { query } from ''itiriri''; const map = new Map(); map.set(1, ''Alice''); map.set(2, ''Bob''); const result = query(map) .filter([k, v] => v.indexOf(''A'') >= 0) .map([k, v] => `k - ${v.toUpperCase()}`); for (const r of result) { console.log(r); // prints: 1 - ALICE }


[...map.entries()] o Array.from(map.entries())

Es superfácil.

De todos modos, los iteradores carecen de reducción, filtro y métodos similares. Tienes que escribirlos por tu cuenta, ya que es más perfumante que convertir Map a array y viceversa. Pero no haga saltos Mapa -> Matriz -> Mapa -> Matriz -> Mapa -> Matriz, porque matará el rendimiento.