The easiest way to merge ES6 cards / sets?

Is there an easy way to combine ES6 cards together (e.g. Object.assign )? And while we are doing this, what about ES6 Sets (e.g. Array.concat )?

+128
javascript set ecmascript-6
Aug 14 '15 at 1:11
source share
11 answers

For sets:

 var merged = new Set([...set1, ...set2, ...set3]) 

For cards:

 var merged = new Map([...map1, ...map2, ...map3]) 

Please note that if several cards have the same key, the value of the combined card will be the value of the last merging card with this key.

+214
Aug 14 '15 at 1:22
source share

Here is my solution using generators:

For cards:

 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' ] ] 

For sets:

 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' ] 
+41
Aug 14 '15 at 3:19
source share

For reasons that I don’t understand, you cannot directly add the contents of one set to another using the built-in operation. Operations such as union, intersection, merging, etc., are fairly simple operations on sets, but are not built-in. Fortunately, you can pretty easily build it all yourself.

Thus, to implement the merge operation (merging the contents of one set into another or one card into another), you can do this with one line .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 



And for Map you can do this:

 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); }); 

Or in ES6 syntax:

 t.forEach((value, key) => s.set(key, value)); 



For your information, if you need a simple subclass of the built-in Set object that contains the .merge() method, you can use this:

 // 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; 

This should be your own setex.js file, which can then be require() placed in node.js and used instead of the built-in set.

+30
Aug 14 '15 at 1:23
source share

Edit :

I compared my original solution to other proposals and found that it was very inefficient.

The benchmark itself is very interesting ( link ). It compares 3 solutions (the higher, the better):

  • @ Bfred.it solution that adds values ​​one after another (14 955 op / s)
  • @ Jameslk solution that uses a self-starting generator (5089 operations per second)
  • my own that uses reduction and distribution (3434 op / s)

As you can see, @ bfred.it's solution is definitely a winner.

Performance + Consistency

Keeping this in mind, here is a slightly modified version that does not change the original set and excludes a variable number of iterations for combining as arguments:

 function union(...iterables) { const set = new Set(); for (let iterable of iterables) { for (let item of iterable) { set.add(item); } } return set; } 

Using:

 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} 



Original answer

I would like to suggest a different approach using the reduce and spread operator:

Implementation

 function union (sets) { return sets.reduce((combined, list) => { return new Set([...combined, ...list]); }, new Set()); } 

Using:

 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} 

Tip:

We can also use the rest statement to make the interface a little better:

 function union (...sets) { return sets.reduce((combined, list) => { return new Set([...combined, ...list]); }, new Set()); } 

Now, instead of passing an array of sets, we can pass an arbitrary number of argument sets:

 union(a, b, c) // {1, 2, 3, 4, 5, 6} 
+16
May 11 '18 at 16:04
source share

The approved answer is great, but each time a new set is created.

If you want to mutate an existing object, use a helper function.

Set

 function concatSets(set, ...iterables) { for (const iterable of iterables) { for (const item of iterable) { set.add(item); } } } 

Using:

 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 

Map

 function concatMaps(map, ...iterables) { for (const iterable of iterables) { for (const item of iterable) { map.set(...item); } } } 

Using:

 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] 
+12
Dec 26 '16 at 8:21
source share

To combine sets in the Sets array, you can do

 var Sets = [set1, set2, set3]; var merged = new Set([].concat(...Sets.map(set => Array.from(set)))); 

It’s a little cryptic to me why the following, which should be equivalent, fails, at least in Babylon:

 var merged = new Set([].concat(...Sets.map(Array.from))); 
+5
Aug 14 '15 at 2:37
source share

Based on Asaf Kats answer, here is a typewritten version:

 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 } 
+1
Jan 30 '19 at 0:39
source share

No, there are no built-in operations for them, but you can easily create them yourself:

 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; }; 
0
Aug 14 '15 at 5:04 on
source share

Example

 const mergedMaps = (...maps) => { const dataMap = new Map([]) for (const map of maps) { for (const [key, value] of map) { dataMap.set(key, value) } } return dataMap } 

Using

 const map = mergedMaps(new Map([[1, false]]), new Map([['foo', 'bar']]), new Map([['lat', 1241.173512]])) Array.from(map.keys()) // [1, 'foo', 'lat'] 
0
Nov 28 '17 at 21:50
source share

It makes no sense to call new Set(...anArrayOrSet) when adding multiple elements (from an array or another set) to an existing set .

I use this in the reduce function and it is just plain stupid. Even if you have a distribution operator ...array , you should not use it in this case, since it wastes CPU, memory and time.

 // 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)) } } 

Demo fragment

 // 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())) 

0
Aug 09 '19 at 12:50
source share

Object.assign can be used to combine maps:

 merged = Object.assign({}, first, second) 
-3
Apr 11 '18 at 8:32
source share



All Articles