Existe uma maneira simples de mesclar o ES6 Maps (como Object.assign
)? E enquanto estamos nisso, o que dizer dos Conjuntos ES6 (como Array.concat
)?
for..of
pois key
pode ser de qualquer tipo #
Existe uma maneira simples de mesclar o ES6 Maps (como Object.assign
)? E enquanto estamos nisso, o que dizer dos Conjuntos ES6 (como Array.concat
)?
for..of
pois key
pode ser de qualquer tipo #
Respostas:
Para conjuntos:
var merged = new Set([...set1, ...set2, ...set3])
Para mapas:
var merged = new Map([...map1, ...map2, ...map3])
Observe que se vários mapas tiverem a mesma chave, o valor do mapa mesclado será o valor do último mapa mesclado com essa chave.
Map
: “Construtor: new Map([iterable])
”, “ iterable
é uma matriz ou outro objeto iterável cujos elementos são pares de valores-chave (matrizes de 2 elementos). Cada par de valores-chave é adicionado ao novo mapa. ” - apenas como referência.
Map
construtor evita o consumo de memória.
Aqui está minha solução usando geradores:
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))
se você quiser suporte ao navegador fácil
Por razões que não entendo, você não pode adicionar diretamente o conteúdo de um conjunto a outro com uma operação interna. Operações como união, interseção, mesclagem, etc ... são operações de conjunto bastante básicas, mas não são integradas. Felizmente, você pode construir tudo isso com bastante facilidade.
Portanto, para implementar uma operação de mesclagem (mesclando o conteúdo de um conjunto em outro ou de um mapa em outro), você pode fazer isso com uma única .forEach()
linha:
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, para um Map
, você pode fazer o seguinte:
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);
});
Ou, na sintaxe do ES6:
t.forEach((value, key) => s.set(key, value));
Para sua informação, se você deseja uma subclasse simples do Set
objeto interno que contém um .merge()
método, pode usar o seguinte:
// 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;
Isso deve estar em seu próprio arquivo setex.js, que você poderá require()
usar no node.js e usar no lugar do conjunto interno.
new Set(s, t)
. trabalho. O t
parâmetro é ignorado. Além disso, obviamente não é um comportamento razoável add
detectar o tipo de seu parâmetro e se um conjunto adicionar os elementos do conjunto, porque não haveria como adicionar um conjunto a um conjunto.
.add()
método de fazer um set, entendo seu ponto de vista. Eu acho isso muito menos útil do que ser capaz de combinar conjuntos usando .add()
como nunca precisei de um conjunto ou conjuntos, mas tive a necessidade de mesclar conjuntos muitas vezes. Apenas uma questão de opinião sobre a utilidade de um comportamento versus o outro.
n.forEach(m.add, m)
- inverte pares chave / valor!
Map.prototype.forEach()
e Map.prototype.set()
tenham invertido argumentos. Parece uma supervisão de alguém. Isso força mais código ao tentar usá-los juntos.
set
a ordem dos parâmetros é natural para pares de chave / valor, forEach
está alinhada com Array
o forEach
método s (e coisas como $.each
ou _.each
que também enumeram objetos).
Editar :
Comparei minha solução original com outras soluções sugeridas aqui e descobri que é muito ineficiente.
O benchmark em si é muito interessante ( link ) Compara 3 soluções (quanto maior, melhor):
- solução do @ bfred.it, que adiciona valores um por um (14.955 op / s)
- solução de @ jameslk, que usa um gerador de auto-invocação (5.089 op / s)
- o meu, que usa reduzir e espalhar (3.434 op / s)
Como você pode ver, a solução da @ bfred.it é definitivamente a vencedora.
Desempenho + Imutabilidade
Com isso em mente, aqui está uma versão ligeiramente modificada que não altera o conjunto original e excede um número variável de iteráveis 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}
Gostaria de sugerir outra abordagem, usando reduce
e o 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}
Dica:
Também podemos usar o rest
operador para tornar a interface um pouco melhor:
function union (...sets) {
return sets.reduce((combined, list) => {
return new Set([...combined, ...list]);
}, new Set());
}
Agora, em vez de passar uma matriz de conjuntos, podemos passar um número arbitrário de argumentos de conjuntos:
union(a, b, c) // {1, 2, 3, 4, 5, 6}
forof
e add
, o que parece ser muito ineficiente. Eu realmente gostaria de um addAll(iterable)
método em Conjuntos
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 }
A resposta aprovada é ótima, mas isso cria um novo conjunto sempre.
Se você deseja modificar um objeto existente, use uma função 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 mesclar os conjuntos na matriz Conjuntos, você pode fazer
var Sets = [set1, set2, set3];
var merged = new Set([].concat(...Sets.map(set => Array.from(set))));
É um pouco misterioso para mim por que o seguinte, que deveria ser equivalente, falha pelo menos em Babel:
var merged = new Set([].concat(...Sets.map(Array.from)));
Array.from
usa parâmetros adicionais, o segundo sendo uma função de mapeamento. Array.prototype.map
passa três argumentos para seu retorno de chamada:, (value, index, array)
portanto está efetivamente chamando Sets.map((set, index, array) => Array.from(set, index, array)
. Obviamente, index
é um número e não uma função de mapeamento, portanto falha.
Não faz sentido chamar new Set(...anArrayOrSet)
ao adicionar vários elementos (de uma matriz ou de outro conjunto) a um conjunto existente .
Eu uso isso em uma reduce
função, e é simplesmente bobo. Mesmo se você tiver o ...array
operador spread disponível, não deve usá-lo nesse caso, pois desperdiça recursos de processador, memória 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()))
Transforme os conjuntos em matrizes, alise-os e, finalmente, o construtor se unificará.
const union = (...sets) => new Set(sets.map(s => [...s]).flat());
Não, não há operações internas para elas, mas você pode criá-las facilmente:
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']
Você pode usar a sintaxe de propagação para mesclá-los:
const map1 = {a: 1, b: 2}
const map2 = {b: 1, c: 2, a: 5}
const mergedMap = {...a, ...b}
=> {a: 5, b: 1, c: 2}
mesclar para mapear
let merge = {...map1,...map2};