Existe-t-il un moyen simple de fusionner des cartes ES6 (comme Object.assign
)? Et tant que nous y sommes, qu'en est-il des ensembles ES6 (comme Array.concat
)?
Pour les ensembles:
var merged = new Set([...set1, ...set2, ...set3])
Pour les cartes:
var merged = new Map([...map1, ...map2, ...map3])
Notez que si plusieurs cartes ont la même clé, la valeur de la carte fusionnée sera celle de la dernière carte fusionnée avec cette clé.
Voici ma solution en utilisant des générateurs:
Pour les cartes:
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' ] ]
Pour les ensembles:
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' ]
Pour des raisons que je ne comprends pas, vous ne pouvez pas ajouter directement le contenu d’un Set à un autre avec une opération intégrée.
Il semblerait naturel que .add()
ait détecté que vous dépassiez un autre objet Set, puis que vous récupériez tous les éléments de cet ensemble (c'est ainsi que mon own set object - avant qu'il y ait eu une spécification ES6 Set) fonctionne . Mais, ils ont choisi de ne pas l'appliquer de cette façon.
Au lieu de cela, vous pouvez le faire avec une seule ligne .forEach()
:
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
Et, pour un Map
, vous pouvez faire ceci:
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);
});
Modifier:
J'ai comparé ma solution initiale à d'autres solutions suggérées ici et constaté qu'elle était très inefficace.
Le benchmark lui-même est très intéressant ( link ) Il compare 3 solutions (le plus haut est le mieux):
- La solution de @ bfred.it, qui ajoute des valeurs une par une (14 955 op/s)
- La solution de @ jameslk, qui utilise un générateur à invocation automatique (5 089 op/s)
- mon propre, qui utilise réduire et répandre (3,434 op/sec)
Comme vous pouvez le constater, la solution de @ bfred.it est définitivement gagnante.
Performance + Immutabilité
Dans cet esprit, voici une version légèrement modifiée qui ne le fait pas mute l'ensemble d'origine et excepte un nombre variable d'itérables vers combiner comme arguments:
function union(...iterables) { const set = new Set(); for (let iterable of iterables) { for (let item of iterable) { set.add(item); } } return set; }
Utilisation:
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}
Je voudrais suggérer une autre approche, en utilisant reduce
et l'opérateur spread
:
function union (sets) {
return sets.reduce((combined, list) => {
return new Set([...combined, ...list]);
}, new Set());
}
Utilisation:
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}
Pointe:
Nous pouvons également utiliser l'opérateur rest
pour rendre l'interface un peu plus agréable:
function union (...sets) {
return sets.reduce((combined, list) => {
return new Set([...combined, ...list]);
}, new Set());
}
Maintenant, au lieu de passer un array d'ensembles, nous pouvons passer un nombre arbitraire de arguments d'ensembles:
union(a, b, c) // {1, 2, 3, 4, 5, 6}
La réponse approuvée est excellente mais crée à chaque fois un nouvel ensemble.
Si vous souhaitez plutôt muter un objet existant, utilisez une fonction d'assistance.
function concatSets(set, ...iterables) {
for (const iterable of iterables) {
for (const item of iterable) {
set.add(item);
}
}
}
Usage:
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);
}
}
}
Usage:
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]
Pour fusionner les ensembles dans le tableau Ensembles, vous pouvez faire
var Sets = [set1, set2, set3];
var merged = new Set([].concat(...Sets.map(set => Array.from(set))));
Il m'est un peu mystérieux pourquoi les raisons suivantes, qui devraient être équivalentes, échouent au moins dans Babel:
var merged = new Set([].concat(...Sets.map(Array.from)));
Non, il n'y a pas d'opération intégrée pour ceux-ci, mais vous pouvez facilement les créer vous-même:
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;
};
Sur la base de la réponse d'Asaf Katz, voici une version TypeScript:
export 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
}
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']
Il n'a aucun sens d'appeler new Set(...anArrayOrSet)
lors de l'ajout de plusieurs éléments (d'un tableau ou d'un autre ensemble) à un ensemble existant.
J'utilise ceci dans une fonction reduce
, et c'est tout simplement idiot. Même si vous disposez de l'opérateur ...array
spread, vous ne devez pas l'utiliser dans ce cas, car cela gaspille du temps, de la mémoire et du processeur.
// 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()))