Javascript 合并 ES6 Maps/Sets 的最简单方法?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/32000865/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Simplest way to merge ES6 Maps/Sets?
提问by jameslk
Is there a simple way to merge ES6 Maps together (like Object.assign
)? And while we're at it, what about ES6 Sets (like Array.concat
)?
有没有一种简单的方法可以将 ES6 Maps 合并在一起(比如Object.assign
)?当我们在做的时候,ES6 Sets(比如Array.concat
)呢?
回答by Oriol
For sets:
对于套装:
var merged = new Set([...set1, ...set2, ...set3])
For maps:
对于地图:
var merged = new Map([...map1, ...map2, ...map3])
Note that if multiple maps have the same key, the value of the merged map will be the value of the last merging map with that key.
请注意,如果多个映射具有相同的键,则合并映射的值将是具有该键的最后一个合并映射的值。
回答by jameslk
Here's my solution using generators:
这是我使用生成器的解决方案:
For Maps:
对于地图:
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' ]
回答by jfriend00
For reasons I do not understand, you cannot directly add the contents of one Set to another with a built-in operation. Operations like union, intersect, merge, etc... are pretty basic set operations, but are not built-in. Fortunately, you can construct these all yourself fairly easily.
由于我不明白的原因,您不能通过内置操作直接将一个 Set 的内容添加到另一个 Set 中。并集、相交、合并等操作是非常基本的集合操作,但不是内置的。幸运的是,您可以很容易地自己构建这些。
So, to implement a merge operation (merging the contents of one Set into another or one Map into another), you can do this with a single .forEach()
line:
因此,要实现合并操作(将一个 Set 的内容合并到另一个 Set 或将一个 Map 合并到另一个),您可以用.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 a Map
, you could do this:
而且,对于 a Map
,你可以这样做:
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:
或者,在 ES6 语法中:
t.forEach((value, key) => s.set(key, value));
FYI, if you want a simple subclass of the built-in Set
object that contains a .merge()
method, you can use this:
仅供参考,如果你想要一个Set
包含.merge()
方法的内置对象的简单子类,你可以使用这个:
// 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 is meant to be in it's own file setex.js that you can then require()
into node.js and use in place of the built-in Set.
这意味着在它自己的文件 setex.js 中,然后您可以将其require()
放入 node.js 并使用它来代替内置的 Set。
回答by Asaf Katz
Edit:
I benchmarked my original solution against other solutions suggests here and found that it is very inefficient.
The benchmark itself is very interesting (link) It compares 3 solutions (higher is better):
- @bfred.it's solution, which adds values one by one (14,955 op/sec)
- @jameslk's solution, which uses a self invoking generator (5,089 op/sec)
- my own, which uses reduce & spread (3,434 op/sec)
As you can see, @bfred.it's solution is definitely the winner.
Performance + Immutability
With that in mind, here's a slightly modified version which doesn't mutates the original set and excepts a variable number of iterables to combine as 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}
编辑:
我根据此处建议的其他解决方案对我的原始解决方案进行了基准测试,发现它非常低效。
基准测试本身非常有趣(链接)它比较了 3 个解决方案(越高越好):
- @bfred.it 的解决方案,将值一一相加(14,955 次操作/秒)
- @jameslk 的解决方案,它使用自调用生成器(5,089 次操作/秒)
- 我自己的,使用 reduce & spread(3,434 次操作/秒)
如您所见,@bfred.it 的解决方案绝对是赢家。
性能 + 不变性
考虑到这一点,这里有一个稍微修改过的版本,它不会改变原始集合,并且将可变数量的可迭代对象组合为参数:
function union(...iterables) { const set = new Set(); for (let iterable of iterables) { for (let item of iterable) { set.add(item); } } return set; }
用法:
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 another approach, using reduce
and the spread
operator:
我想建议另一种方法,使用reduce
和spread
运营商:
Implementation
执行
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}
Tip:
提示:
We can also make use of the rest
operator to make the interface a bit nicer:
我们还可以利用rest
操作符让界面更好看:
function union (...sets) {
return sets.reduce((combined, list) => {
return new Set([...combined, ...list]);
}, new Set());
}
Now, instead of passing an arrayof sets, we can pass an arbitrary number of argumentsof sets:
现在,我们可以传递任意数量的集合参数,而不是传递一组集合:
union(a, b, c) // {1, 2, 3, 4, 5, 6}
回答by fregante
The approved answer is great but that creates a new set every time.
批准的答案很好,但每次都会创建一个新的集合。
If you want to mutatean existing object instead, use a helper function.
如果您想改变现有对象,请使用辅助函数。
Set
放
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
Map
地图
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]
回答by fregante
To merge the sets in the array Sets, you can do
要合并数组 Sets 中的集合,您可以执行
var Sets = [set1, set2, set3];
var merged = new Set([].concat(...Sets.map(set => Array.from(set))));
It is slightly mysterious to me why the following, which should be equivalent, fails at least in Babel:
我有点神秘,为什么以下应该是等效的,至少在 Babel 中失败了:
var merged = new Set([].concat(...Sets.map(Array.from)));
回答by ndp
Based off of Asaf Katz's answer, here's a typescript version:
基于 Asaf Katz 的回答,这里有一个打字稿版本:
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
}
回答by Steven Spungin
It does not make any senseto call new Set(...anArrayOrSet)
when adding multiple elements (from either an array or another set) to an existing set.
它没有任何意义调用new Set(...anArrayOrSet)
当添加多个元素(来自阵列或另一组)到现有的一组。
I use this in a reduce
function, and it is just plain silly. Even if you have the ...array
spread operator available, you should not use it in this case, as it wastes processor, memory, and time resources.
我在一个reduce
函数中使用它,这很愚蠢。即使您有...array
可用的扩展运算符,在这种情况下也不应该使用它,因为它会浪费处理器、内存和时间资源。
// 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 Snippet
演示片段
// 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()))
回答by Danilo Santos
merge to Map
合并到地图
let merge = {...map1,...map2};
回答by Nadiar AS
Example:
例子:
const map1 = {a: 1, b: 2}
const map2 = {b: 1, c: 2, a: 5}
const mergedMap = {...a, ...b}
=> {a: 5, b: 1, c: 2}