¿Hay una manera simple de fusionar ES6 Maps (como Object.assign
)? Y mientras estamos en eso, ¿qué pasa con los conjuntos ES6 (como Array.concat
)?
for..of
porque key
puede ser de cualquier tipo
¿Hay una manera simple de fusionar ES6 Maps (como Object.assign
)? Y mientras estamos en eso, ¿qué pasa con los conjuntos ES6 (como Array.concat
)?
for..of
porque key
puede ser de cualquier tipo
Respuestas:
Para conjuntos:
var merged = new Set([...set1, ...set2, ...set3])
Para mapas:
var merged = new Map([...map1, ...map2, ...map3])
Tenga en cuenta que si varios mapas tienen la misma clave, el valor del mapa fusionado será el valor del último mapa fusionado con esa clave.
Map
: "Constructor: new Map([iterable])
", " iterable
es una matriz u otro objeto iterable cuyos elementos son pares clave-valor (matrices de 2 elementos). Cada par clave-valor se agrega al nuevo Mapa ". - Solo como referencia.
Map
constructor evita su consumo de memoria.
Aquí está mi solución usando generadores:
Para mapas:
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' ] ]
Para conjuntos:
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))
si quieres un soporte de navegador fácil
Por razones que no entiendo, no puede agregar directamente el contenido de un conjunto a otro con una operación integrada. Operaciones como unión, intersección, fusión, etc. son operaciones de conjunto bastante básicas, pero no están integradas. Afortunadamente, puede construir todo esto usted mismo con bastante facilidad.
Entonces, para implementar una operación de fusión (fusionar el contenido de un Conjunto en otro o un Mapa en otro), puede hacer esto con una sola .forEach()
línea:
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
Y, por un lado Map
, podrías hacer esto:
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);
});
O, en la sintaxis de ES6:
t.forEach((value, key) => s.set(key, value));
Para su información, si desea una subclase simple del Set
objeto incorporado que contiene un .merge()
método, puede usar esto:
// 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;
Está destinado a estar en su propio archivo setex.js que luego puede require()
ingresar a node.js y usar en lugar del conjunto incorporado.
new Set(s, t)
. trabajos. El t
parámetro se ignora. Además, obviamente no es un comportamiento razonable tener que add
detectar el tipo de su parámetro y si un conjunto agrega los elementos del conjunto, porque entonces no habría forma de agregar un conjunto a un conjunto.
.add()
método que toma un Set, entiendo tu punto. Simplemente encuentro que es mucho menos útil que poder combinar conjuntos usando .add()
ya que nunca he necesitado un Conjunto o Conjuntos, pero he tenido la necesidad de fusionar conjuntos muchas veces. Solo es una cuestión de opinión sobre la utilidad de un comportamiento frente al otro.
n.forEach(m.add, m)
invierte pares clave / valor!
Map.prototype.forEach()
y Map.prototype.set()
han invertido argumentos. Parece un descuido de alguien. Fuerza más código cuando intenta usarlos juntos.
set
El orden de los parámetros es natural para los pares clave / valor, forEach
está alineado con Array
el forEach
método s (y cosas como $.each
o _.each
que también enumeran objetos).
Editar :
Comparé mi solución original con otras soluciones sugeridas aquí y descubrí que es muy ineficiente.
El punto de referencia en sí es muy interesante ( enlace ) Compara 3 soluciones (más alto es mejor):
- La solución de @ bfred.it, que agrega valores uno por uno (14,955 op / seg)
- La solución de @jameslk, que utiliza un generador de auto invocación (5.089 op / seg)
- el mío, que usa reducción y propagación (3,434 op / seg)
Como puede ver, la solución de @ bfred.it es definitivamente la ganadora.
Rendimiento + Inmutabilidad
Con eso en mente, aquí hay una versión ligeramente modificada que no muta el conjunto original y exceptúa un número variable de iterables para combinar como argumentos:
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}
Me gustaría sugerir otro enfoque, el uso reduce
y el spread
operador:
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}
Propina:
También podemos hacer uso del rest
operador para que la interfaz sea un poco más agradable:
function union (...sets) {
return sets.reduce((combined, list) => {
return new Set([...combined, ...list]);
}, new Set());
}
Ahora, en lugar de pasar una serie de conjuntos, podemos pasar un número arbitrario de argumentos de conjuntos:
union(a, b, c) // {1, 2, 3, 4, 5, 6}
forof
y add
, lo que parece muy ineficiente. Realmente deseo un addAll(iterable)
método en 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 }
La respuesta aprobada es excelente, pero eso crea un nuevo conjunto cada vez.
Si en su lugar desea mutar un objeto existente, use una función auxiliar.
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]
Para fusionar los conjuntos en la matriz Conjuntos, puede hacer
var Sets = [set1, set2, set3];
var merged = new Set([].concat(...Sets.map(set => Array.from(set))));
Es un poco misterioso para mí por qué lo siguiente, que debería ser equivalente, falla al menos en Babel:
var merged = new Set([].concat(...Sets.map(Array.from)));
Array.from
toma parámetros adicionales, el segundo es una función de mapeo. Array.prototype.map
pasa tres argumentos a su devolución de llamada: (value, index, array)
por lo que está llamando efectivamente Sets.map((set, index, array) => Array.from(set, index, array)
. Obviamente, index
es un número y no una función de mapeo, por lo que falla.
No tiene ningún sentido llamar new Set(...anArrayOrSet)
cuando se agregan múltiples elementos (desde una matriz u otro conjunto) a un conjunto existente .
Lo uso en una reduce
función, y es simplemente tonto. Incluso si tiene el ...array
operador de propagación disponible, no debe usarlo en este caso, ya que desperdicia recursos de procesador, memoria y tiempo.
// 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()))
Transforme los conjuntos en matrices, aplánelos y finalmente el constructor se unificará.
const union = (...sets) => new Set(sets.map(s => [...s]).flat());
No, no hay operaciones integradas para estas, pero puede crearlas fácilmente:
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']
Puede usar la sintaxis de propagación para fusionarlos:
const map1 = {a: 1, b: 2}
const map2 = {b: 1, c: 2, a: 5}
const mergedMap = {...a, ...b}
=> {a: 5, b: 1, c: 2}
fusionar al mapa
let merge = {...map1,...map2};