Esiste un modo semplice per unire insieme ES6 Maps (come Object.assign
)? E mentre ci siamo, che dire dei set ES6 (come Array.concat
)?
for..of
perché key
può essere di qualsiasi tipo
Esiste un modo semplice per unire insieme ES6 Maps (come Object.assign
)? E mentre ci siamo, che dire dei set ES6 (come Array.concat
)?
for..of
perché key
può essere di qualsiasi tipo
Risposte:
Per set:
var merged = new Set([...set1, ...set2, ...set3])
Per le mappe:
var merged = new Map([...map1, ...map2, ...map3])
Se più mappe hanno la stessa chiave, il valore della mappa unita sarà il valore dell'ultima mappa unita con quella chiave.
Map
: “Costruttore: new Map([iterable])
”, “ iterable
è una matrice o altro oggetto iterabile i cui elementi sono coppie chiave-valore (matrici a 2 elementi). Ogni coppia chiave-valore viene aggiunta alla nuova mappa. " - solo come riferimento.
Map
costruttore evita il loro consumo di memoria.
Ecco la mia soluzione usando i generatori:
Per le mappe:
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' ] ]
Per set:
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))
se vuoi un facile supporto per il browser
Per motivi che non capisco, non è possibile aggiungere direttamente il contenuto di un set a un altro con un'operazione integrata. Operazioni come unione, intersezione, unione, ecc ... sono operazioni di set piuttosto basilari, ma non sono integrate. Fortunatamente, puoi costruirli abbastanza da solo.
Quindi, per implementare un'operazione di unione (unendo il contenuto di un Set in un altro o una Mappa in un altro), puoi farlo con una sola .forEach()
riga:
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
E, per a Map
, potresti farlo:
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);
});
Oppure, nella sintassi ES6:
t.forEach((value, key) => s.set(key, value));
Cordiali saluti, se si desidera una sottoclasse semplice Set
dell'oggetto incorporato che contiene un .merge()
metodo, è possibile utilizzare questo:
// 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;
Questo è pensato per essere nel suo file setex.js che puoi quindi require()
in node.js e usare al posto del Set integrato.
new Set(s, t)
. lavori. Il t
parametro viene ignorato. Inoltre, non è ovviamente ragionevole avere il add
rilevamento del tipo del suo parametro e se un set aggiunge gli elementi del set, perché non ci sarebbe modo di aggiungere un set stesso a un set.
.add()
metodo di presa di un set, capisco il tuo punto. Trovo che sia molto meno utile che riuscire a combinare set usando .add()
come non ho mai avuto bisogno di un Set o Set, ma ho avuto bisogno di unire più set. È solo una questione di opinione sull'utilità di un comportamento rispetto all'altro.
n.forEach(m.add, m)
- inverte coppie chiave / valore!
Map.prototype.forEach()
e Map.prototype.set()
hanno argomenti invertiti. Sembra una svista da parte di qualcuno. Forza più codice quando si cerca di usarli insieme.
set
l'ordine dei parametri è naturale per le coppie chiave / valore, forEach
è allineato con Array
il forEach
metodo s (e cose simili $.each
o _.each
che elencano anche gli oggetti).
modificare :
Ho confrontato la mia soluzione originale con altre soluzioni suggerite qui e ho scoperto che è molto inefficiente.
Il benchmark stesso è molto interessante ( link ) Confronta 3 soluzioni (più alto è meglio):
- La soluzione di @ bfred.it, che aggiunge i valori uno per uno (14.955 op / sec)
- La soluzione di @ jameslk, che utilizza un generatore auto invocante (5.089 op / sec)
- il mio, che utilizza riduci e diffondi (3.434 op / sec)
Come puoi vedere, la soluzione di @ bfred.it è sicuramente il vincitore.
Prestazioni + immutabilità
Con questo in mente, ecco una versione leggermente modificata che non muta il set originale ed esclude un numero variabile di iterabili da combinare come argomenti:
function union(...iterables) { const set = new Set(); for (let iterable of iterables) { for (let item of iterable) { set.add(item); } } return set; }
Uso:
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}
Vorrei suggerire un altro approccio, usando reduce
e ilspread
operatore:
function union (sets) {
return sets.reduce((combined, list) => {
return new Set([...combined, ...list]);
}, new Set());
}
Uso:
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}
Mancia:
Possiamo anche utilizzare l' rest
operatore per rendere l'interfaccia un po 'più gradevole:
function union (...sets) {
return sets.reduce((combined, list) => {
return new Set([...combined, ...list]);
}, new Set());
}
Ora, invece di passare una matrice di insiemi, possiamo passare un numero arbitrario di argomenti di insiemi:
union(a, b, c) // {1, 2, 3, 4, 5, 6}
forof
e add
, il che sembra molto inefficiente. Vorrei davvero un addAll(iterable)
metodo sui set
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 }
La risposta approvata è ottima, ma crea sempre un nuovo set.
Se vuoi mutare un oggetto esistente, usa una funzione di aiuto.
function concatSets(set, ...iterables) {
for (const iterable of iterables) {
for (const item of iterable) {
set.add(item);
}
}
}
Uso:
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);
}
}
}
Uso:
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]
Per unire i set nei set di array, puoi farlo
var Sets = [set1, set2, set3];
var merged = new Set([].concat(...Sets.map(set => Array.from(set))));
È leggermente misterioso per me il motivo per cui quanto segue, che dovrebbe essere equivalente, fallisce almeno in Babele:
var merged = new Set([].concat(...Sets.map(Array.from)));
Array.from
accetta parametri aggiuntivi, il secondo è una funzione di mappatura. Array.prototype.map
passa tre argomenti al suo callback:, (value, index, array)
quindi sta effettivamente chiamando Sets.map((set, index, array) => Array.from(set, index, array)
. Ovviamente, index
è un numero e non una funzione di mappatura, quindi fallisce.
Non ha senso chiamare new Set(...anArrayOrSet)
quando si aggiungono più elementi (da un array o da un altro set) a un set esistente .
Lo uso in una reduce
funzione ed è semplicemente stupido. Anche se hai l' ...array
operatore di diffusione disponibile, in questo caso non dovresti usarlo, poiché spreca risorse di processore, memoria e tempo.
// 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()))
Trasforma gli insiemi in array, appiattiscili e infine il costruttore si unificherà.
const union = (...sets) => new Set(sets.map(s => [...s]).flat());
No, non ci sono operazioni integrate per queste, ma puoi facilmente crearle tue:
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']
È possibile utilizzare la sintassi di diffusione per unirli insieme:
const map1 = {a: 1, b: 2}
const map2 = {b: 1, c: 2, a: 5}
const mergedMap = {...a, ...b}
=> {a: 5, b: 1, c: 2}
Unisci alla mappa
let merge = {...map1,...map2};