Comment mapper / réduire / filtrer un ensemble en JavaScript?

132

Existe-t-il un moyen de map/ reduce/ filter/ etc a Seten JavaScript ou devrai-je écrire le mien?

Voici quelques Set.prototypeextensions sensibles

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

Prenons un petit ensemble

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

Et quelques petites fonctions stupides

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

Et voyez comment ils fonctionnent

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

N'est-ce pas sympa? Ouais, je le pense aussi. Comparez cela à l'utilisation laide de l'itérateur

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

Et

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

Existe-t-il un meilleur moyen d'accomplir mapet d' reduceutiliser un SetJavaScript?

Je vous remercie
la source
Le problème avec la réduction de carte Setest que les ensembles ne sont pas des fonctions.
Bartek Banachewicz
@BartekBanachewicz ouais c'est un peu un problème ... non?
Merci
2
Eh bien, réfléchissez var s = new Set([1,2,3,4]); s.map((a) => 42);. Cela change le nombre d'éléments, ce qui mapn'est généralement pas censé faire. Pire encore si vous ne comparez que des parties des objets conservés, car techniquement, il n'est pas spécifié lequel vous obtiendrez.
Bartek Banachewicz
J'avais envisagé cela, mais je ne suis pas sûr que je considérerais (personnellement) cela comme invalide. OK donc au moins forEachexiste pour ce scénario, mais pourquoi non reducealors?
Merci
4
Quelques lectures connexes: esdiscuss.org/topic/set-some-every-reduce-filter-map-methods
CodingIntrigue

Réponses:

105

Un moyen abrégé de le faire est de le convertir en un tableau via l'opérateur de propagation ES6.

Ensuite, toutes les fonctions du tableau sont à votre disposition.

const mySet = new Set([1,2,3,4]);
[...mySet].reduce()
ZephDavies
la source
1
Parce que les fonctions ne sont pas disponibles pour Set! Il s'agit d'une solution de contournement complète, guidée et comprise qui n'est pas encore présente dans cette rubrique. Le fait que cela `` prenne plus de temps '' est un triste prix à payer pour une solution de contournement jusqu'à ce que Set implémente ces fonctionnalités!
ZephDavies
1
Quelle est la différence entre cela et Array.from
pete
9
Pour moi au moins, la différence entre ceci et Array.fromc'est que cela Array.fromfonctionne avec TypeScript. L'utilisation [...mySet]donne l'erreur:TS2461: Type 'Set<number>' is not an array type.
Mikal Madsen
1
Pour spread vs Array.from (), voir stackoverflow.com/a/40549565/5516454 Fondamentalement, les deux sont utilisables ici. Array.from () peut en plus faire des objets de type tableau qui n'implémentent pas la @@iteratorméthode.
ZephDavies
ne fonctionne toujours pas pour moi avec dactylographié. Je reçoisERROR TypeError: this.sausages.slice is not a function
Simon_Weaver
22

Pour résumer la discussion à partir des commentaires: bien qu'il n'y ait aucune raison technique pour que set ne l' ait pasreduce , il n'est pas actuellement fourni et nous ne pouvons qu'espérer qu'il changera dans ES7.

Quant à lui map, l'appeler seul pourrait violer la Setcontrainte, sa présence ici pourrait donc être discutable.

Envisagez de mapper avec une fonction (a) => 42- cela changera la taille de l'ensemble à 1, et cela pourrait ou non être ce que vous vouliez.

Si vous êtes d'accord pour violer cela parce que, par exemple, vous allez vous plier de toute façon, vous pouvez appliquer la mappartie sur chaque élément juste avant de les transmettre reduce, acceptant ainsi que la collection intermédiaire ( qui n'est pas un ensemble à ce stade ) soit va être réduit peut avoir des éléments en double. C'est essentiellement équivalent à la conversion en Array pour effectuer le traitement.

Bartek Banachewicz
la source
1
C'est généralement bon, sauf que (en utilisant le code ci-dessus), s.map(a => 42)cela entraînera Set { 42 }que le résultat mappé aura une longueur différente mais qu'il n'y aura pas d'éléments "dupliqués". Peut-être mettre à jour le libellé et j'accepterai cette réponse.
Merci
@naomik Oh derp J'étais juste en train de finir mon premier café en écrivant ça. Sur le deuxième regard, la collection intermédiaire passée à réduire peut avoir des éléments immédiats si vous acceptez que ce ne soit pas un ensemble - c'est ce que je voulais dire.
Bartek Banachewicz
Oh, je comprends - la carte doit correspondre au même type, d'où des collisions possibles dans l'ensemble de destination. Quand j'ai trouvé cette question, je pensais que la carte serait mappée à un tableau à partir d'un ensemble. (comme si vous aviez défini set.toArray (). map () `
Simon_Weaver
2
Dans Scala et Haskell, les ensembles prennent en charge une opération de carte - cela peut réduire le nombre d'éléments dans l'ensemble.
Velizar Hristov
8

La cause du manque de map/ reduce/ filteron Map/ Setcollections semble être principalement des préoccupations conceptuelles. Si chaque type de collection en Javascript spécifie réellement ses propres méthodes itératives uniquement pour permettre

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

au lieu de

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

Une alternative était de spécifier map / reduction / filter dans le cadre du protocole iterable / iterator, puisque entries/ values/ keysreturn Iterators. Il est cependant concevable que tous les itérables ne soient pas également «mappables». Une autre alternative était de spécifier un «protocole de collecte» distinct à cette fin.

Cependant, je ne connais pas la discussion actuelle sur ce sujet à ES.


la source