Existe-t-il un moyen simple de fusionner des cartes ES6 ensemble (comme Object.assign
)? Et pendant que nous y sommes, qu'en est-il des ensembles ES6 (comme Array.concat
)?
for..of
car il key
peut s'agir de n'importe quel type
Existe-t-il un moyen simple de fusionner des cartes ES6 ensemble (comme Object.assign
)? Et pendant que nous y sommes, qu'en est-il des ensembles ES6 (comme Array.concat
)?
for..of
car il key
peut s'agir de n'importe quel type
Réponses:
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 la valeur de la dernière carte fusionnée avec cette clé.
Map
: «Constructor: new Map([iterable])
», « iterable
est un tableau ou un autre objet itérable dont les éléments sont des paires clé-valeur (tableaux à 2 éléments). Chaque paire clé-valeur est ajoutée à la nouvelle carte. » - juste comme référence.
Map
constructeur évite sa consommation de mémoire.
Voici ma solution 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' ]
m2.forEach((k,v)=>m1.set(k,v))
si vous voulez un support de navigateur facile
Pour des raisons que je ne comprends pas, vous ne pouvez pas ajouter directement le contenu d'un ensemble à un autre avec une opération intégrée. Les opérations comme l'union, l'intersection, la fusion, etc. sont des opérations d'ensemble assez basiques, mais ne sont pas intégrées. Heureusement, vous pouvez tout construire vous-même assez facilement.
Ainsi, pour implémenter une opération de fusion (fusionner le contenu d'un ensemble dans un autre ou d'une carte dans une autre), vous pouvez le faire avec une seule .forEach()
ligne:
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);
});
Ou, dans la syntaxe ES6:
t.forEach((value, key) => s.set(key, value));
Pour info, si vous voulez une simple sous-classe de l' Set
objet intégré qui contient une .merge()
méthode, vous pouvez utiliser ceci:
// 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;
Ceci est censé être dans son propre fichier setex.js que vous pouvez ensuite require()
utiliser dans node.js et utiliser à la place du Set intégré.
new Set(s, t)
. travaux. Le t
paramètre est ignoré. Aussi, ce n'est évidemment pas un comportement raisonnable d'avoir add
détecté le type de son paramètre et si un ensemble ajoute les éléments de l'ensemble, car alors il n'y aurait aucun moyen d'ajouter un ensemble lui-même à un ensemble.
.add()
méthode prenant un Set, je comprends votre point. Je trouve juste que cela est beaucoup moins utile que de pouvoir combiner des ensembles en utilisant .add()
car je n'ai jamais eu besoin d'un ensemble ou d'ensembles, mais j'ai eu besoin de fusionner des ensembles plusieurs fois. Juste une question d'opinion sur l'utilité d'un comportement par rapport à l'autre.
n.forEach(m.add, m)
- cela inverse les paires clé / valeur!
Map.prototype.forEach()
et Map.prototype.set()
avoir des arguments inversés. Cela ressemble à un oubli de quelqu'un. Cela force plus de code lorsque vous essayez de les utiliser ensemble.
set
L'ordre des paramètres est naturel pour les paires clé / valeur, forEach
est aligné sur la méthode Array
s forEach
(et des choses comme $.each
ou _.each
qui énumèrent également des objets).
Modifier :
J'ai comparé ma solution initiale à d'autres solutions suggérées ici et j'ai trouvé qu'elle était très inefficace.
Le benchmark en lui-même est très intéressant ( lien ) Il compare 3 solutions (plus haut est mieux):
- la solution de @ bfred.it, qui ajoute les valeurs une par une (14 955 op / s)
- la solution de @ jameslk, qui utilise un générateur auto-appelant (5089 op / s)
- le mien, qui utilise réduire et étaler (3,434 op / sec)
Comme vous pouvez le voir, la solution de @ bfred.it est définitivement la gagnante.
Performance + Immuabilité
Dans cet esprit, voici une version légèrement modifiée qui ne mute pas l'ensemble d'origine et exclut un nombre variable d'itérables à 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; }
Usage:
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 proposer une autre approche, en utilisant reduce
et l' spread
opérateur:
function union (sets) {
return sets.reduce((combined, list) => {
return new Set([...combined, ...list]);
}, new Set());
}
Usage:
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' rest
opérateur 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 tableau d'ensembles, nous pouvons passer un nombre arbitraire d' arguments d'ensembles:
union(a, b, c) // {1, 2, 3, 4, 5, 6}
forof
et add
, qui semble tout simplement très inefficace. Je souhaite vraiment une addAll(iterable)
méthode sur les 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 réponse approuvée est excellente, mais cela crée un nouvel ensemble à chaque fois.
Si vous souhaitez faire muter un objet existant à la place, 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 est un peu mystérieux pour moi pourquoi ce qui suit, qui devrait être équivalent, échoue au moins à Babel:
var merged = new Set([].concat(...Sets.map(Array.from)));
Array.from
prend des paramètres supplémentaires, le second étant une fonction de mappage. Array.prototype.map
passe trois arguments à son callback:, (value, index, array)
donc il appelle effectivement Sets.map((set, index, array) => Array.from(set, index, array)
. De toute évidence, il index
s'agit d'un nombre et non d'une fonction de mappage, donc cela échoue.
Cela n'a aucun sens d'appeler new Set(...anArrayOrSet)
lors de l'ajout de plusieurs éléments (à partir d'un tableau ou d'un autre ensemble) à un ensemble existant .
J'utilise ceci dans une reduce
fonction, et c'est tout simplement idiot. Même si vous disposez de l' ...array
opérateur de diffusion, vous ne devez pas l'utiliser dans ce cas, car il gaspille des ressources de processeur, de mémoire et de temps.
// 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()))
Transformez les ensembles en tableaux, aplatissez-les et finalement le constructeur s'unifiera.
const union = (...sets) => new Set(sets.map(s => [...s]).flat());
Non, il n'y a pas d'opérations intégrées 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;
};
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']
Vous pouvez utiliser la syntaxe de diffusion pour les fusionner:
const map1 = {a: 1, b: 2}
const map2 = {b: 1, c: 2, a: 5}
const mergedMap = {...a, ...b}
=> {a: 5, b: 1, c: 2}
fusionner avec la carte
let merge = {...map1,...map2};