Wie ordne / reduziere / filtere ich ein Set in JavaScript?

128

Gibt es eine Möglichkeit zu map/ reduce/ filter/ etc a Setin JavaScript oder muss ich meine eigene schreiben?

Hier sind einige sinnvolle Set.prototypeErweiterungen

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

Nehmen wir ein kleines Set

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

Und einige dumme kleine Funktionen

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

Und sehen, wie sie funktionieren

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

Ist das nicht schön Ja, das denke ich auch. Vergleichen Sie das mit der hässlichen Iterator-Verwendung

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

Und

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

Gibt es eine bessere Möglichkeit, eine in JavaScript zu erreichen mapund zu reduceverwenden Set?

Danke dir
quelle
Das Problem bei der Kartenreduzierung Setist, dass Sets keine Funktoren sind.
Bartek Banachewicz
@BartekBanachewicz Ja, das ist ein Problem ... richtig?
Vielen Dank, dass Sie
2
Nun, überlegen Sie var s = new Set([1,2,3,4]); s.map((a) => 42);. Es ändert die Anzahl der Elemente, was mapnormalerweise nicht der Fall ist. Noch schlimmer, wenn Sie nur Teile der aufbewahrten Objekte vergleichen, denn technisch ist nicht angegeben, welche Sie erhalten.
Bartek Banachewicz
Ich hatte darüber nachgedacht, bin mir aber nicht sicher, ob ich das (persönlich) für ungültig halten würde. OK, also gibt es zumindest forEachfür dieses Szenario, aber warum reducedann nicht?
Vielen Dank, dass Sie
4
Einige verwandte Lektüre: esdiscuss.org/topic/set-some-every-reduce-filter-map-methods
CodingIntrigue

Antworten:

101

Eine kurze Möglichkeit besteht darin, es über den ES6-Spread-Operator in ein Array zu konvertieren.

Dann stehen Ihnen alle Array-Funktionen zur Verfügung.

const mySet = new Set([1,2,3,4]);
[...mySet].reduce()
ZephDavies
quelle
Weil die Funktionen für Set! Nicht verfügbar sind. Dies ist eine vollständige, geführte und verstandene Problemumgehung, die in diesem Thema noch nicht vorhanden ist. Die Tatsache, dass es "länger dauert", ist ein trauriger Preis für eine Problemumgehung, bis Set diese Funktionen implementiert!
ZephDavies
1
Was ist der Unterschied zwischen diesem und Array.from
pete
9
Zumindest für mich ist der Unterschied zwischen diesem und dem, Array.fromdass es Array.frommit TypeScript funktioniert. Verwenden [...mySet]gibt den Fehler:TS2461: Type 'Set<number>' is not an array type.
Mikal Madsen
1
Informationen zu Spread vs Array.from () finden Sie unter stackoverflow.com/a/40549565/5516454. Grundsätzlich können beide hier verwendet werden. Array.from () kann zusätzlich Array-ähnliche Objekte ausführen, die die @@iteratorMethode nicht implementieren .
ZephDavies
funktioniert bei mir immer noch nicht mit maschinenskript. Ich bekommeERROR TypeError: this.sausages.slice is not a function
Simon_Weaver
22

Um die Diskussion von Kommentaren Fazit: während es keine technischen Gründe für Satz sind nicht hat reduce, ist es derzeit nicht zur Verfügung gestellt und wir können nur hoffen , dass es in ES7 ändert.

Wenn Sie mapes alleine nennen, kann dies die SetEinschränkung verletzen , sodass seine Anwesenheit hier möglicherweise umstritten ist.

Ziehen Sie eine Zuordnung mit einer Funktion in Betracht (a) => 42- dadurch wird die Größe des Sets auf 1 geändert, und dies könnte das sein, was Sie wollten oder nicht .

Wenn Sie damit einverstanden sind, weil Sie z. B. sowieso falten, können Sie das mapTeil auf jedes Element anwenden, bevor Sie es übergeben, reduceund so akzeptieren, dass die Zwischensammlung ( die zu diesem Zeitpunkt kein Set ist ) das ist reduziert werden könnte doppelte Elemente haben. Dies entspricht im Wesentlichen der Konvertierung in ein Array für die Verarbeitung.

Bartek Banachewicz
quelle
1
Dies ist vor allem gut, mit Ausnahme von (den obigen Code verwendet wird ), s.map(a => 42)wird in Folge Set { 42 }so das abgebildete Ergebnis eine andere Länge sein wird , aber es wird nicht „dupliziert“ Elemente. Vielleicht aktualisieren Sie den Wortlaut und ich werde diese Antwort akzeptieren.
Vielen Dank, dass Sie
@naomik Oh derp, ich habe gerade meinen ersten Kaffee getrunken, als ich das geschrieben habe. Auf den zweiten Blick könnte die zum Reduzieren übergebene Zwischensammlung unmittelbare Elemente enthalten, wenn Sie akzeptieren, dass es sich nicht um eine Menge handelt - das habe ich gemeint.
Bartek Banachewicz
Oh, ich verstehe - die Karte muss dem gleichen Typ zugeordnet werden, daher mögliche Kollisionen im Zielsatz. Als ich diese Frage fand, dachte ich, dass Map einem Array aus einem Set zugeordnet werden würde. (als ob Sie set.toArray (). map () `
Simon_Weaver
2
In Scala und Haskell unterstützen Sets eine Kartenoperation - sie kann die Anzahl der Elemente im Set reduzieren.
Velizar Hristov
8

Die Ursache für den Mangel an map/ reduce/ filteron Map/ SetSammlungen scheinen hauptsächlich konzeptionelle Bedenken zu sein. Sollte jeder Sammlungstyp in Javascript tatsächlich seine eigenen iterativen Methoden angeben, nur um dies zuzulassen

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

anstatt

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

Eine Alternative bestand darin, map / redu / filter als Teil des iterable / iterator-Protokolls anzugeben, da entries/ values/ keysreturn Iterators. Es ist jedoch denkbar, dass nicht jedes iterable auch "abbildbar" ist. Eine andere Alternative bestand darin, zu diesem Zweck ein separates "Sammelprotokoll" anzugeben.

Die aktuelle Diskussion zu diesem Thema bei ES kenne ich jedoch nicht.


quelle