如何将值数组添加到集合中

223

将数组中所有值加入 Set 的旧方式是:

// for the sake of this example imagine this set was created somewhere else 
// and I cannot construct a new one out of an array
let mySet = new Set()

for(let item of array) {
  mySet.add(item)
}

有没有更优雅的方法来做这个?也许是mySet.add(array)mySet.add(...array)

PS:我知道两者都不行


26
Set.prototype.addAll有一个一阶段的提案,详情请见Set.prototype.addAll - Yury Tarabanko
1
@YuryTarabanko 这有点奇怪,为什么一开始没有将其包含在Set#add中。肯定有人认为Array#push接受多个参数是不好的。 - Robert
1
你不能使用Set构造函数吗?请参考https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Set#Relation_with_Array_objects。 - TrueWill
3
当我们使用arr.push(...items)时,为什么还需要使用set.add(value)呢?我最近遇到了这个问题,API设计不太好。 - Sainath S.R
11个回答

220

虽然 Set API 仍然非常简约,但你可以使用 Array.prototype.forEach 缩短代码:

array.forEach(item => mySet.add(item))

// alternative, without anonymous arrow function
array.forEach(mySet.add, mySet)

8
我认为这并不比使用 for of 循环更好。特别是第二个选项更加令人困惑。 - Timmmm
140
我喜欢糟糕的API被称为_极简主义_! - ruX
3
在 array.forEach(mySet.add, mySet) 中,为什么需要第二个参数? - ed22
6
请参阅forEach文档: thisArg(可选)-在执行回调函数时要使用的this值。 - amankkg
4
我永远无法习惯mySet.add方法不使用点号前对象的上下文,而是像一个完全独立的方法。 - ed22
显示剩余9条评论

112

这里有一种功能性的方法,返回一个新的集合:

const set = new Set(['a', 'b', 'c'])
const arr = ['d', 'e', 'f']
const extendedSet = new Set([ ...set, ...arr ])
// Set { 'a', 'b', 'c', 'd', 'e', 'f' }

使用这种方法,我最终得到了嵌套的多级集合和数组,而不是一个扁平化的集合。 - Paulo Neves
23
@PauloNeves,听起来你没有使用展开语法... - Julien
@PauloNeves 每次添加新的项目数组时,他都会复制Set的内容。当然要使用扩展语法“...”。 - Alok Prakash
这个语法比较新,但听起来像是我们在复制...对象的副本,换句话说最终创建一个新的集合并插入所有元素。 - Manohar Reddy Poreddy

42

在我看来,这是最优雅的。

// for a new Set 
const x = new Set([1,2,3,4]);

// for an existing Set
const y = new Set();

[1,2,3,4].forEach(y.add, y);

又一个优雅的! - Fuzzyma
3
forEach 比遍历数组慢大约200倍。如果只对少量值进行操作,那么使用它是可以接受的,但如果你需要经常这样做,请注意可能会出现性能问题。https://dev59.com/wlcP5IYBdhLWcg3wys-G - Ryan Shillington
18
@RyanShillington所说并不正确,虽然曾经是这样,但自从那篇回答发布以来,浏览器已经进行了很多优化。请参见https://jsben.ch/fxaYy(感谢@Redu)。 - WiR3D
5
@WiR3D 哇,天啊。那是个好消息。 - Ryan Shillington
1
整洁干净,我喜欢它! - undg

16
如何使用展开运算符将新数组项轻松地混合到现有集合中?
let mySet = new Set([1,2,3,4])
const additionalSet = [5,6,7,8,9]
mySet = new Set([...mySet, ...additionalSet])

[JSFIDDLE][1] [1]: https://jsfiddle.net/clayperez/yjkxh9d8/9/

[JSFIDDLE][1] [1]: https://jsfiddle.net/clayperez/yjkxh9d8/9/


8
再说一遍:“显然你没有阅读我在代码中注释的评论,我在那里说明集合是在其他地方创建的,我不能创建一个新的集合。” - Fuzzyma
3
将“const” mySet 更改为“let”。 - Francisco Castro

10

你也可以使用 Array.reduce() 方法:

const mySet = new Set();
mySet.add(42); // Just to illustrate that an existing Set is used

[1, 2, 3].reduce((s, e) => s.add(e), mySet);

39
这里使用 reduce 没有意义,当你只需要迭代 forEach 时。请参考 @WiR3D 的答案。 - Paul Irish
2
我不太明白你的反对意见。有时候你想要一个单语句的解决方案,比如在lambda中你不想要花括号块,或者在初始化中。虽然reduce可能不是最明显的选择,但我看不出它为什么不“合理”——你正在将一个数组缩减成其他东西——一个Set——这正是reduce的预期用途。 - JHH
2
@JHH 的意思是 [1, 2, 3].forEach(e => mySet.add(e)) 或者 for (const e of [1, 2, 3]) mySet.add(e) 更有意义。 - CherryDT
1
@CherryDT 我认为我们必须就“有意义”的含义达成不同意见。我个人不会选择reduce解决方案,但我将其添加为一个选项。很惊讶有19个人认为这不“有意义”。 - JHH
4
foreach不是一个表达式,然而reduce是一个表达式,因此你可以一次性地返回和规约(reduce)。 - mindlid
显示剩余4条评论

4

创建一个新的 Set:

    //Existing Set
    let mySet = new Set([1,2,3,4,5]);
    //Existing Array
    let array = [6,7,8,9,0];
        
    mySet = new Set(array.concat([...mySet]));
    console.log([...mySet]);
    
    //or single line
    console.log([...new Set([6,7,8,9,0].concat([...new Set([1,2,3,4,5])]))]);


这种 spread 和 concat 的组合有什么优势吗?我认为 Julien 的回答更易懂。 - Robert
@Robert,数组已经存在,在我的示例中,我是从现有的数组+现有的Set创建新的集合,Julien的示例中,他从另外两个现有对象创建一个新数组来创建第四个对象,这不是一个好的做法。 - montelof
Array#concat 也会创建一个新的数组。你的解决方案用不同的代码表达了相同的意思,但是拥有选择总是好的。(array.concat([...set]) vs [...array, ...set] - Robert

3

在此提供灵感...创建一个扩展Set的类,并添加addRange方法。

    class MegaSet extends Set {
    
      constructor(iterable) {
       super(iterable);
      }
      
      addRange(range) {
        for (var elem of range) {
          this.add(elem);
        }
      }
    }
    
    const array = [1,2,3,5,5,6];
    let mySet = new MegaSet([1,2,3,4]);
    
    mySet.addRange(array);
    console.log([...mySet]);


1
答案通常更好,如果它包括解释所包含代码的文本。你可能也对“为什么扩展原生对象是一种不好的方法?”这个问题的答案感兴趣。 - Heretic Monkey
1
你没错。我已经更新了我的代码,提供了一个可能的解决方案...即使在这种特定情况下,集合已经被实例化了。 - Thomas-Louis Simard
如果您能安全地扩展原型,那是我认为更好的方法(就像Mike在发布链接之前说的通常是可以的)。 - baao

0

在创建集合时,首先输入数组。您将获得包含数组中所有项的集合。

const array = [1,2,3,4,5]
let mySet = new Set(array)
console.log(mySet)
//Add new one element
mySet.add(6)
console.log(mySet)

//Add exist element
mySet.add(6)
console.log(mySet)

这不是“将数组添加到集合中”。这是“从数组创建一个新的集合”。 - Mostafa Zeinali

0
let mySet = new Set(['a', 'b']);
let arrayToBeAdded = ['c', 'd'];

//convert the Set to an array, then concatenate both arrays, finally convert into a Set
mySet = Array.form(mySet).concat(arrayToBeAdded);
mySet = new Set(mySet);

//in single line
mySet = new Set(Array.form(mySet).concat(arrayToBeAdded));

-1

目前在Sets中没有addAll方法,但是当你使用它们时,有两个选项可以简化你的生活。第一个选项是扩展原型。在这样做之前,请阅读这篇文章,然后决定可能的后果是否适合你的项目/预期使用。

if (!Set.prototype.addAll) {
  Set.prototype.addAll = function(items) {
    if (!Array.isArray(items)) throw new TypeError('passed item is not an array');
    // or (not sure what the real Set.prototype will get sometime)
    // if (!Array.isArray(items)) items = [items];
    for (let it of items) {
      this.add(it);
    }
    return this;
  }
}

如果您决定不扩展原型,只需创建一个函数,在项目中可以重复使用。
function addAll(_set, items) {
    // check set and items
    for (let it of items) {
         _set.add(it);
    }
    return _set;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接