¿Cómo mapear / reducir / filtrar un conjunto en JavaScript?

131

¿Hay alguna forma de map/ reduce/ filter/ etc a Seten JavaScript o tendré que escribir la mía?

Aquí hay algunas Set.prototypeextensiones sensatas

Set.prototype.map = function map(f) {
  var newSet = new Set();
  for (var v of this.values()) newSet.add(f(v));
  return newSet;
};

Set.prototype.reduce = function(f,initial) {
  var result = initial;
  for (var v of this) result = f(result, v);
  return result;
};

Set.prototype.filter = function filter(f) {
  var newSet = new Set();
  for (var v of this) if(f(v)) newSet.add(v);
  return newSet;
};

Set.prototype.every = function every(f) {
  for (var v of this) if (!f(v)) return false;
  return true;
};

Set.prototype.some = function some(f) {
  for (var v of this) if (f(v)) return true;
  return false;
};

Tomemos un pequeño set

let s = new Set([1,2,3,4]);

Y algunas estúpidas funciones

const times10 = x => x * 10;
const add = (x,y) => x + y;
const even = x => x % 2 === 0;

Y mira cómo funcionan

s.map(times10);    //=> Set {10,20,30,40}
s.reduce(add, 0);  //=> 10
s.filter(even);    //=> Set {2,4}
s.every(even);     //=> false
s.some(even);      //=> true

¿No es lindo? Sí, yo también lo creo. Compare eso con el uso del iterador feo

// puke
let newSet = new Set();
for (let v in s) {
  newSet.add(times10(v));
}

Y

// barf
let sum = 0;
for (let v in s) {
  sum = sum + v;
}

¿Hay alguna forma mejor de lograr mapy reduceusar un SetJavaScript?

Gracias
fuente
El problema con la reducción de mapas a Setes que los Conjuntos no son Functores.
Bartek Banachewicz
@BartekBanachewicz, sí, eso es un problema ... ¿verdad?
Gracias
2
Bueno, considéralo var s = new Set([1,2,3,4]); s.map((a) => 42);. Cambia el número de elementos, que mapnormalmente no se supone que debe hacer. Peor aún si solo está comparando partes de los objetos guardados, porque técnicamente no se especifica cuál obtendrá.
Bartek Banachewicz
Lo había considerado, pero no estoy seguro de que (personalmente) lo considere inválido. OK, al menos forEachexiste para ese escenario, pero ¿por qué no reduceentonces?
Gracias
44
Algunas lecturas relacionadas: esdiscuss.org/topic/set-some-every-reduce-filter-map-methods
CodingIntrigue

Respuestas:

105

Una forma abreviada de hacerlo es convertirlo en una matriz a través del operador de propagación ES6.

Entonces todas las funciones de la matriz están disponibles para usted.

const mySet = new Set([1,2,3,4]);
[...mySet].reduce()
ZephDavies
fuente
1
¡Porque las funciones no están disponibles para Set! Esta es una solución completa, guiada y comprendida que aún no está presente en este tema. ¡El hecho de que 'tome más tiempo' es un precio triste para pagar una solución alternativa hasta que Set implemente estas características!
ZephDavies
1
¿Cuál es la diferencia entre esto y Array.from
Pete
9
Al menos para mí, la diferencia entre esto y Array.fromeso es que Array.fromfunciona con TypeScript. El uso [...mySet]da el error:TS2461: Type 'Set<number>' is not an array type.
Mikal Madsen
1
Para spread vs Array.from (), consulte stackoverflow.com/a/40549565/5516454 Básicamente, ambos son utilizables aquí. Array.from () también puede hacer objetos tipo matriz que no implementan el @@iteratormétodo.
ZephDavies el
Todavía no me funciona con mecanografiado. Me saleERROR TypeError: this.sausages.slice is not a function
Simon_Weaver
22

Para resumir la discusión de los comentarios: si bien no hay razones técnicas para que el conjunto no tengareduce , actualmente no se proporciona y solo podemos esperar que cambie en ES7.

En cuanto a map, llamarlo solo podría violar elSet restricción, por lo que su presencia aquí podría ser discutible.

Considere mapear con una función (a) => 42: cambiará el tamaño del conjunto a 1, y esto podría o no ser lo que deseaba.

Si está de acuerdo con violar eso porque, por ejemplo, va a doblar de todos modos, puede aplicar la mapparte en cada elemento justo antes de pasarlos reduce, aceptando así que la colección intermedia ( que no es un Conjunto en este punto ) es podría reducirse podría tener elementos duplicados. Esto es esencialmente equivalente a convertir a Array para hacer el procesamiento.

Bartek Banachewicz
fuente
1
Esto es mayormente bueno, excepto que (usando el código anterior), s.map(a => 42)resultará en Set { 42 }que el resultado mapeado tendrá una longitud diferente pero no habrá elementos "duplicados". Tal vez actualice la redacción y aceptaré esta respuesta.
Gracias
@naomik Oh derp, estaba terminando mi primer café cuando escribía eso. En el segundo aspecto, la colección intermedia aprobada para reducir podría tener elementos inmediatos si acepta que no es un conjunto, a eso me refiero.
Bartek Banachewicz
Oh, lo entiendo: el mapa debe asignarse al mismo tipo, por lo tanto, posibles colisiones en el conjunto de destino. Cuando encontré esta pregunta, estaba pensando que map se asignaría a una matriz de un conjunto. (como si hubiera configurado.toArray (). map () `
Simon_Weaver
2
En Scala y Haskell, los conjuntos admiten una operación de mapa: puede reducir la cantidad de elementos en el conjunto.
Velizar Hristov
8

La causa de la falta de map/ reduce/ filteren Map/ Setcolecciones parecen ser principalmente preocupaciones conceptuales. Si cada tipo de colección en Javascript especifica realmente sus propios métodos iterativos solo para permitir esto

const mySet = new Set([1,2,3]);
const myMap = new Map([[1,1],[2,2],[3,3]]);

mySet.map(x => x + 1);
myMap.map(([k, x]) => [k, x + 1]);

en vez de

new Set(Array.from(mySet.values(), x => x + 1));
new Map(Array.from(myMap.entries(), ([k, x]) => [k, x + 1]));

Una alternativa era especificar map / reduce / filter como parte del protocolo iterable / iterator, ya que entries / values/ keysreturnIterator s. Sin embargo, es concebible que no todos los iterables sean "mapeables". Otra alternativa era especificar un "protocolo de recolección" separado para este mismo propósito.

Sin embargo, no conozco la discusión actual sobre este tema en ES.


fuente