2015-10-20 3 views
33

Gibt es einen Weg zu map/reduce/filter/etc eine Set in JavaScript oder muss ich meine eigene schreiben?Wie wird ein Set in JavaScript gemappt/reduziert/gefiltert?

Hier einige vernünftige Set.prototype Erweiterungen

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

Werfen wir einen kleinen Satz

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 

Isn't that nice ? Yeah, I think so too. Compare that to the ugly iterator usage

// 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 Art und Weise map und reduce mit einem Set in JavaScript zu erreichen?

+0

Das Problem mit dem Map-Reduzieren eines 'Set' ist, dass Sets keine Functors sind. –

+0

@BartekBanachewicz Ja, das ist ein Problem ... oder? – naomik

+1

Nun, betrachte 'var s = new Set ([1,2,3,4]); s.map ((a) => 42); '. Es ändert die Anzahl der Elemente, die "map" normalerweise nicht tun soll. Noch schlimmer, wenn Sie nur Teile der aufbewahrten Objekte vergleichen, denn dann ist technisch nicht bekannt, welches Sie erhalten werden. –

Antwort

10

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

Wie für map, kann das Aufrufen von es allein die Set Einschränkung verletzen, so dass seine Anwesenheit hier strittig sein könnte.

Betrachten Sie die Zuordnung mit einer Funktion (a) => 42 - es wird die Größe des Sets auf 1 ändern, und dies könnte oder möglicherweise nicht sein, was Sie wollten.

Wenn Sie damit einverstanden sind, weil z. Sie werden trotzdem falten, Sie können den map Teil auf jedes Element anwenden, kurz bevor Sie sie an reduce übergeben, und damit akzeptieren, dass die Zwischensammlung (, die kein Set ist an diesem Punkt), die reduziert werden könnte doppelte Elemente. Dies entspricht im Wesentlichen der Umwandlung in Array, um die Verarbeitung durchzuführen.

+0

Das ist meistens gut, außer dass (mit dem obigen Code) 's.map (a => 42)' zu 'Set {42}' führt, so dass das gemappte Ergebnis eine andere Länge hat, aber nicht "dupliziert" wird "Elemente. Vielleicht die Formulierung aktualisieren und ich werde diese Antwort akzeptieren. – naomik

+0

@naomik Oh derp Ich beendete gerade meinen ersten Kaffee, als ich das schrieb. Auf den zweiten Blick, die Zwischenkollektion, die weitergegeben wurde, um * zu reduzieren, * könnte * unmittelbare Elemente haben, wenn Sie akzeptieren, dass es kein Satz ist - das ist gemeint. –

3

Die Ursache für den Mangel an map/reduce/filter auf Map/Set Sammlungen scheinen vor allem konzeptionelle Bedenken zu sein. Sollte jede Art Sammlung in Javascript tatsächlich seine eigene iterative Methoden geben Sie nur diese

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

statt

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

Eine Alternative zu zuließe Karte angeben/reduzieren/Filter als Teil des iterable/Iteratorprotokoll, seit entries/values/keys zurück Iterator s. Es ist jedoch denkbar, dass nicht jedes Iterable auch "mappbar" ist. Eine andere Alternative bestand darin, für diesen Zweck ein separates "Sammlungsprotokoll" anzugeben.

Allerdings kenne ich die aktuelle Diskussion zu diesem Thema bei ES nicht.

15

Eine Möglichkeit, dies zu tun, besteht darin, sie über den ES6-Spread-Operator in ein Array umzuwandeln.

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

const mySet = new Set([1,2,3,4]); 
[...mySet].reduce() 
+0

Da die Funktionen nicht für Set 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 Umgehung, bis Set diese Funktionen implementiert! – ZephDavies

+0

Was ist der Unterschied zwischen dieser und Array.von – pete

+1

Für mich zumindest, der Unterschied zwischen diesem und "Array.from" ist, dass "Array.from" mit TypeScript funktioniert. Die Verwendung von '[... mySet]' gibt den Fehler: 'TS2461: Typ 'Set ' ist kein Array-Typ.' –