Gibt es eine einfache Möglichkeit, ES6-Karten zusammenzuführen (wie Object.assign
)? Und wenn wir schon dabei sind, was ist mit ES6-Sets (wie Array.concat
)?
for..of
weil key
jeder Typ sein kann
Gibt es eine einfache Möglichkeit, ES6-Karten zusammenzuführen (wie Object.assign
)? Und wenn wir schon dabei sind, was ist mit ES6-Sets (wie Array.concat
)?
for..of
weil key
jeder Typ sein kann
Antworten:
Für Sets:
var merged = new Set([...set1, ...set2, ...set3])
Für Karten:
var merged = new Map([...map1, ...map2, ...map3])
Beachten Sie, dass der Wert der zusammengeführten Karte der Wert der zuletzt zusammengeführten Karte mit diesem Schlüssel ist, wenn mehrere Karten denselben Schlüssel haben.
Map
: „Konstruktor: new Map([iterable])
“, „ iterable
ist ein Array oder ein anderes iterierbares Objekt, dessen Elemente Schlüssel-Wert-Paare sind (2-Element-Arrays). Jedes Schlüssel-Wert-Paar wird der neuen Karte hinzugefügt. “ - Nur als Referenz.
Map
Konstruktor wird dessen Speicherverbrauch vermieden.
Hier ist meine Lösung mit Generatoren:
Für Karten:
let map1 = new Map(), map2 = new Map();
map1.set('a', 'foo');
map1.set('b', 'bar');
map2.set('b', 'baz');
map2.set('c', 'bazz');
let map3 = new Map(function*() { yield* map1; yield* map2; }());
console.log(Array.from(map3)); // Result: [ [ 'a', 'foo' ], [ 'b', 'baz' ], [ 'c', 'bazz' ] ]
Für Sets:
let set1 = new Set(['foo', 'bar']), set2 = new Set(['bar', 'baz']);
let set3 = new Set(function*() { yield* set1; yield* set2; }());
console.log(Array.from(set3)); // Result: [ 'foo', 'bar', 'baz' ]
m2.forEach((k,v)=>m1.set(k,v))
wenn du einfache browser unterstützung
Aus Gründen, die ich nicht verstehe, können Sie den Inhalt eines Sets nicht direkt mit einer integrierten Operation zu einem anderen hinzufügen. Operationen wie Vereinigung, Überschneiden, Zusammenführen usw. sind ziemlich einfache Mengenoperationen, aber nicht integriert. Glücklicherweise können Sie diese alle ziemlich einfach selbst konstruieren.
Um einen Zusammenführungsvorgang zu implementieren (Zusammenführen des Inhalts eines Sets in einem anderen oder einer Map in einem anderen), können Sie dies mit einer einzelnen .forEach()
Zeile tun :
var s = new Set([1,2,3]);
var t = new Set([4,5,6]);
t.forEach(s.add, s);
console.log(s); // 1,2,3,4,5,6
Und zum einen Map
könnten Sie dies tun:
var s = new Map([["key1", 1], ["key2", 2]]);
var t = new Map([["key3", 3], ["key4", 4]]);
t.forEach(function(value, key) {
s.set(key, value);
});
Oder in der ES6-Syntax:
t.forEach((value, key) => s.set(key, value));
Zu Ihrer Information, wenn Sie eine einfache Unterklasse des integrierten Set
Objekts möchten , das eine .merge()
Methode enthält , können Sie Folgendes verwenden:
// subclass of Set that adds new methods
// Except where otherwise noted, arguments to methods
// can be a Set, anything derived from it or an Array
// Any method that returns a new Set returns whatever class the this object is
// allowing SetEx to be subclassed and these methods will return that subclass
// For this to work properly, subclasses must not change behavior of SetEx methods
//
// Note that if the contructor for SetEx is passed one or more iterables,
// it will iterate them and add the individual elements of those iterables to the Set
// If you want a Set itself added to the Set, then use the .add() method
// which remains unchanged from the original Set object. This way you have
// a choice about how you want to add things and can do it either way.
class SetEx extends Set {
// create a new SetEx populated with the contents of one or more iterables
constructor(...iterables) {
super();
this.merge(...iterables);
}
// merge the items from one or more iterables into this set
merge(...iterables) {
for (let iterable of iterables) {
for (let item of iterable) {
this.add(item);
}
}
return this;
}
// return new SetEx object that is union of all sets passed in with the current set
union(...sets) {
let newSet = new this.constructor(...sets);
newSet.merge(this);
return newSet;
}
// return a new SetEx that contains the items that are in both sets
intersect(target) {
let newSet = new this.constructor();
for (let item of this) {
if (target.has(item)) {
newSet.add(item);
}
}
return newSet;
}
// return a new SetEx that contains the items that are in this set, but not in target
// target must be a Set (or something that supports .has(item) such as a Map)
diff(target) {
let newSet = new this.constructor();
for (let item of this) {
if (!target.has(item)) {
newSet.add(item);
}
}
return newSet;
}
// target can be either a Set or an Array
// return boolean which indicates if target set contains exactly same elements as this
// target elements are iterated and checked for this.has(item)
sameItems(target) {
let tsize;
if ("size" in target) {
tsize = target.size;
} else if ("length" in target) {
tsize = target.length;
} else {
throw new TypeError("target must be an iterable like a Set with .size or .length");
}
if (tsize !== this.size) {
return false;
}
for (let item of target) {
if (!this.has(item)) {
return false;
}
}
return true;
}
}
module.exports = SetEx;
Dies soll in der eigenen Datei setex.js sein, die Sie dann require()
in node.js einfügen und anstelle des integrierten Sets verwenden können.
new Set(s, t)
. funktioniert. Der t
Parameter wird ignoriert. Es ist auch offensichtlich kein vernünftiges Verhalten, add
den Typ seines Parameters zu erkennen und wenn eine Menge die Elemente der Menge hinzufügt, da es dann keine Möglichkeit gibt, eine Menge selbst zu einer Menge hinzuzufügen.
.add()
Methode betrifft, mit der ein Set erstellt wird, verstehe ich Ihren Standpunkt. Ich finde nur, dass dies weitaus weniger nützlich ist, als Sets kombinieren zu können, .add()
da ich noch nie ein Set oder Sets benötigt habe, aber ich musste Sets oft zusammenführen. Nur eine Frage der Meinung über die Nützlichkeit eines Verhaltens gegenüber dem anderen.
n.forEach(m.add, m)
- Es invertiert Schlüssel / Wert-Paare!
Map.prototype.forEach()
und Map.prototype.set()
Argumente umgekehrt haben. Scheint wie ein Versehen von jemandem. Es erzwingt mehr Code, wenn versucht wird, sie zusammen zu verwenden.
set
Die Parameterreihenfolge ist für Schlüssel / Wert-Paare natürlich und forEach
richtet sich nach der Methode Array
s forEach
(und nach Dingen wie $.each
oder _.each
die auch Objekte aufzählen).
Bearbeiten :
Ich habe meine ursprüngliche Lösung mit anderen hier vorgeschlagenen Lösungen verglichen und festgestellt, dass sie sehr ineffizient ist.
Der Benchmark selbst ist sehr interessant ( Link ). Er vergleicht 3 Lösungen (höher ist besser):
- @ bfred.its Lösung, die Werte nacheinander addiert (14.955 op / sec)
- @ jameslks Lösung, die einen selbstaufrufenden Generator verwendet (5.089 op / sec)
- meine eigene, die Reduce & Spread verwendet (3.434 op / sec)
Wie Sie sehen können, ist die Lösung von @ bfred.it definitiv der Gewinner.
Leistung + Unveränderlichkeit
In diesem Sinne ist hier eine leicht modifizierte Version, die die ursprüngliche Menge nicht mutiert und eine variable Anzahl von Iterablen ausnimmt, die als Argumente kombiniert werden sollen:
function union(...iterables) { const set = new Set(); for (let iterable of iterables) { for (let item of iterable) { set.add(item); } } return set; }
Verwendung:
const a = new Set([1, 2, 3]); const b = new Set([1, 3, 5]); const c = new Set([4, 5, 6]); union(a,b,c) // {1, 2, 3, 4, 5, 6}
Ich möchte einen anderen Ansatz vorschlagen, der reduce
und den spread
Operator verwendet:
function union (sets) {
return sets.reduce((combined, list) => {
return new Set([...combined, ...list]);
}, new Set());
}
Verwendung:
const a = new Set([1, 2, 3]);
const b = new Set([1, 3, 5]);
const c = new Set([4, 5, 6]);
union([a, b, c]) // {1, 2, 3, 4, 5, 6}
Trinkgeld:
Wir können auch den rest
Operator verwenden, um die Oberfläche ein bisschen schöner zu machen:
function union (...sets) {
return sets.reduce((combined, list) => {
return new Set([...combined, ...list]);
}, new Set());
}
Anstatt ein Array von Mengen zu übergeben, können wir jetzt eine beliebige Anzahl von Argumenten von Mengen übergeben:
union(a, b, c) // {1, 2, 3, 4, 5, 6}
forof
und zu suchen add
, was einfach sehr ineffizient zu sein scheint. Ich wünsche mir wirklich eine addAll(iterable)
Methode für Sets
function union<T> (...iterables: Array<Set<T>>): Set<T> { const set = new Set<T>(); iterables.forEach(iterable => { iterable.forEach(item => set.add(item)) }) return set }
Die genehmigte Antwort ist großartig, aber das schafft jedes Mal ein neues Set.
Wenn Sie stattdessen ein vorhandenes Objekt mutieren möchten , verwenden Sie eine Hilfsfunktion.
function concatSets(set, ...iterables) {
for (const iterable of iterables) {
for (const item of iterable) {
set.add(item);
}
}
}
Verwendung:
const setA = new Set([1, 2, 3]);
const setB = new Set([4, 5, 6]);
const setC = new Set([7, 8, 9]);
concatSets(setA, setB, setC);
// setA will have items 1, 2, 3, 4, 5, 6, 7, 8, 9
function concatMaps(map, ...iterables) {
for (const iterable of iterables) {
for (const item of iterable) {
map.set(...item);
}
}
}
Verwendung:
const mapA = new Map().set('S', 1).set('P', 2);
const mapB = new Map().set('Q', 3).set('R', 4);
concatMaps(mapA, mapB);
// mapA will have items ['S', 1], ['P', 2], ['Q', 3], ['R', 4]
Sie können die Sets in den Array-Sets zusammenführen
var Sets = [set1, set2, set3];
var merged = new Set([].concat(...Sets.map(set => Array.from(set))));
Es ist mir etwas rätselhaft, warum das Folgende, das gleichwertig sein sollte, zumindest in Babel versagt:
var merged = new Set([].concat(...Sets.map(Array.from)));
Array.from
nimmt zusätzliche Parameter an, wobei der zweite eine Zuordnungsfunktion ist. Array.prototype.map
Übergibt drei Argumente an seinen Rückruf : (value, index, array)
, also ruft er effektiv auf Sets.map((set, index, array) => Array.from(set, index, array)
. Offensichtlich index
ist eine Zahl und keine Zuordnungsfunktion, so dass es fehlschlägt.
Es macht keinen Sinn , aufzurufen, new Set(...anArrayOrSet)
wenn mehrere Elemente (entweder aus einem Array oder einem anderen Satz) zu einem vorhandenen Satz hinzugefügt werden .
Ich benutze dies in einer reduce
Funktion, und es ist einfach nur albern. Selbst wenn Sie den ...array
Spread-Operator zur Verfügung haben, sollten Sie ihn in diesem Fall nicht verwenden, da er Prozessor-, Speicher- und Zeitressourcen verschwendet.
// Add any Map or Set to another
function addAll(target, source) {
if (target instanceof Map) {
Array.from(source.entries()).forEach(it => target.set(it[0], it[1]))
} else if (target instanceof Set) {
source.forEach(it => target.add(it))
}
}
// Add any Map or Set to another
function addAll(target, source) {
if (target instanceof Map) {
Array.from(source.entries()).forEach(it => target.set(it[0], it[1]))
} else if (target instanceof Set) {
source.forEach(it => target.add(it))
}
}
const items1 = ['a', 'b', 'c']
const items2 = ['a', 'b', 'c', 'd']
const items3 = ['d', 'e']
let set
set = new Set(items1)
addAll(set, items2)
addAll(set, items3)
console.log('adding array to set', Array.from(set))
set = new Set(items1)
addAll(set, new Set(items2))
addAll(set, new Set(items3))
console.log('adding set to set', Array.from(set))
const map1 = [
['a', 1],
['b', 2],
['c', 3]
]
const map2 = [
['a', 1],
['b', 2],
['c', 3],
['d', 4]
]
const map3 = [
['d', 4],
['e', 5]
]
const map = new Map(map1)
addAll(map, new Map(map2))
addAll(map, new Map(map3))
console.log('adding map to map',
'keys', Array.from(map.keys()),
'values', Array.from(map.values()))
Transformieren Sie die Sets in Arrays, reduzieren Sie sie und schließlich wird der Konstruktor vereinheitlicht.
const union = (...sets) => new Set(sets.map(s => [...s]).flat());
Nein, es gibt keine integrierten Operationen für diese, aber Sie können sie ganz einfach selbst erstellen:
Map.prototype.assign = function(...maps) {
for (const m of maps)
for (const kv of m)
this.add(...kv);
return this;
};
Set.prototype.concat = function(...sets) {
const c = this.constructor;
let res = new (c[Symbol.species] || c)();
for (const set of [this, ...sets])
for (const v of set)
res.add(v);
return res;
};
const mergedMaps = (...maps) => {
const dataMap = new Map([])
for (const map of maps) {
for (const [key, value] of map) {
dataMap.set(key, value)
}
}
return dataMap
}
const map = mergedMaps(new Map([[1, false]]), new Map([['foo', 'bar']]), new Map([['lat', 1241.173512]]))
Array.from(map.keys()) // [1, 'foo', 'lat']
Sie können die Spread-Syntax verwenden , um sie zusammenzuführen:
const map1 = {a: 1, b: 2}
const map2 = {b: 1, c: 2, a: 5}
const mergedMap = {...a, ...b}
=> {a: 5, b: 1, c: 2}
mit Karte zusammenführen
let merge = {...map1,...map2};