【问题标题】:How to add an array of values to a Set如何将值数组添加到 Set
【发布时间】:2018-06-15 19:10:21
【问题描述】:

将数组的所有值添加到 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:我知道两者都不起作用

【问题讨论】:

  • 有一个针对Set.prototype.addAll的阶段1提案
  • @YuryTarabanko 有点奇怪,为什么它一开始就没有包含在Set#add 中。一定有人认为Array#push 接受多个参数是一件坏事。
  • 不能用set构造函数吗?请参阅与数组对象的关系下的developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
  • 啊,当我们有 arr.push(...items) 为什么有 set.add(value) : / 。我最近绊倒了这个。糟糕的 API 设计

标签: javascript set


【解决方案1】:

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

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

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

【讨论】:

  • 添加了不创建箭头函数的版本
  • 我认为这真的比 for of 循环更好。尤其是第二个更令人困惑。
  • 我喜欢 poor API 被称为 minimalistic 的方式!
  • 为什么我们需要第二个参数:array.forEach(mySet.add, mySet)?
  • @ed22 see forEach docs: thisArg(可选) - 执行回调时用作this的值
【解决方案2】:

这是一个函数式的方式,返回一个新的集合:

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

【讨论】:

  • 通过这种方法,我最终得到了嵌套的多级集和数组,而不是扁平集
  • @PauloNeves 听起来你没有使用扩展语法...
【解决方案3】:

这是IMO最优雅的

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

【讨论】:

  • 又一个优雅的!
  • forEach 比遍历数组慢约 200 倍。对于一些值来说这很好,但要注意这是否是你一直在做的事情。 stackoverflow.com/q/43821759/491553
  • @RyanShillington 不正确,它曾经是,但自从发布该答案以来,浏览器已经优化了很多。见jsben.ch/fxaYy(归功于@Redu)
  • @WiR3D 好吧,天哪。这真是个好消息。
【解决方案4】:

如何使用扩展运算符轻松地将新数组项混合到现有集合中?

const mySet = new Set([1,2,3,4])
const additionalSet = [5,6,7,8,9]
mySet = new Set([...mySet, ...additionalSet])

JSFIDDLE

【讨论】:

  • 再一次:“显然你没有阅读代码中的注释,我说该集合是在其他地方创建的,我无法创建一个新的”
  • 将“const”mySet 更改为“let”
【解决方案5】:

你也可以使用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);

【讨论】:

  • 当您只需要 forEach 的迭代时,在这里使用 reduce 没有意义。请参阅@WiR3D 的回答。
  • 我真的不明白你的反对意见。有时您需要一个单语句解决方案,例如在不需要花括号块的 lambda 中,或者在初始化中。虽然reduce 可能不是最明显的选择,但我看不出它是如何“有意义”的——你正在将一个数组缩减为其他东西——一个集合——这正是reduce 的目的。
  • 我会说你是在映射它,而不是在这里减少它。
  • @JHH 重点是[1, 2, 3].forEach(e => mySet.add(e))for (const e of [1, 2, 3]) mySet.add(e) 更有意义。
  • @CherryDT 我认为我们必须同意不同意有意义的含义。我个人不会选择 reduce 解决方案,但我将其添加为一个选项。令 19 人感到惊讶,他们认为这没有“意义”。
【解决方案6】:

创建一个新集合:

    //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的组合有优势吗?我认为朱利安的回答更容易理解。
  • @Robert 数组已经存在,在我的示例中,我从现有数组 + 现有集合创建新集合,Julien 示例他从其他两个现有对象创建一个新数组以创建第四个对象不是一个好习惯。
  • Array#concat 也会创建一个新数组。你的解决方案用不同的代码表达了同样的东西,但有一个选择肯定总是好的。 (array.concat([...set])[...array, ...set]
【解决方案7】:

只是在这里发布灵感.. 创建一个扩展 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]);

【讨论】:

  • 如果包含解释所包含代码的文本,答案通常会更好。您可能也对Why is extending native objects a bad practice?的答案感兴趣。
  • 你没有错。我已经用可能的解决方案更新了我的代码.. 即使在这种特定情况下,该集合已经实例化。
  • 如果您能安全地扩展原型,恕我直言,这是更好的方法(就像您在 Mike 发布链接之前所做的那样)
【解决方案8】:

目前没有适用于 Set 的 addAll 方法,但使用它们时您有两个选项可以简化您的生活。第一个是扩展原型。在您这样做之前,read this post 并在之后决定可能的后果是否适合您的项目/预期用途。

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

【讨论】:

    【解决方案9】:

    @Fuzzyma,我建议你使用 JavaScript 的 PrototypingSet 上定义新方法。

    不要使用 Set 上定义的内置方法名称。

    如果您仍然喜欢使用与 add 这样的内置函数名称相同的函数名称,那么更好的方法是继承 Set 并覆盖 add() 方法。

    这是在不影响其方法的情况下向现有对象添加方法并使用我们自己的同名方法的更好方法。 方法覆盖的魅力,一个不错的 OOP 概念。

    在下面的代码中,我在 Set 上定义了addItems()

    通过http://rextester.com/LGPQC98007在线试用。

    var arr = [3, 7, 8, 75, 65, 32, 98, 32, 3];
    var array = [100, 3, 200, 98, 65, 300]; 
    
    // Create a Set
    var mySet = new Set(arr);
    console.log(mySet);
    
    // Adding items of array to mySet
    Set.prototype.addItems = function(array) {
        for(var item of array){
            this.add(item)
        }
    }
    
    mySet.addItems(array);
    console.log(mySet)
    

    » 输出

    Set { 3, 7, 8, 75, 65, 32, 98 }
    Set { 3, 7, 8, 75, 65, 32, 98, 100, 200, 300 }
    

    【讨论】:

    • 显然您没有阅读代码中的注释,我说该集合是在其他地方创建的,我无法创建一个新的
    • 好的。意思是说您已经创建了一个 Set 并且您想将任何值添加到其中?让我更新我的代码并让你知道。感谢您提醒我的错误。
    • @Fuzzyma,我认为您应该使用 JavaScript 的原型设计功能在 Set 上定义一种方法。确保不要使用 Set 上定义的内置方法名称。我已经用小例子更新了我的答案。请检查它让我知道您的建议。
    • 您可能想看看Why is extending native objects a bad practice?。此外,如果您使用较少的格式,您的答案(和 cmets)将更具可读性。最后,Stack Overflow 有一个称为 Stack Snippets 的功能。在编辑器中,您可以单击带尖括号 (<>) 的按钮并将您的 JavaScript 代码粘贴到其中。这将使它可以为其他人运行。
    • @Mike,我已经在我的代码中提到了这一点,即使我没有使用它。实际上我正在使用手机,所以我无法在这里看到 JS 编辑器。谢谢。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-10-15
    • 2018-11-01
    • 1970-01-01
    • 2021-04-28
    • 1970-01-01
    • 2017-06-08
    相关资源
    最近更新 更多