【问题标题】:JS - Using Set() in Array with ObjectsJS - 在带有对象的数组中使用 Set()
【发布时间】:2018-09-24 00:08:13
【问题描述】:

如何在对象中使用 Set()?我阅读了文档,它适用于数组中的值,但不适用于对象。为什么?我想避免具有完全相同值的重复项。

let nameList_one = [{ name: 'harry', id: 1 }, { name: 'john', id: 2 }, { 'name': 'trevor', id: 3  }]
let nameList_two = [{ name: 'harry', id: 1  }, { name: 'ron', id: 4  }, { name: 'jackson', id: 5  }]

let fullNameList = [...nameList_one , ...nameList_two ]

let filteredList = [...new Set(fullNameList)] // the output is the merge of two array, duplicating "harry".

输出:

[{ name: 'harry', id: 1 }, { name: 'john', id: 2 }, { 'name': 'trevor', id: 3 }, { name: 'harry', id : 1 }, { name: 'ron', id: 4 }, { name: 'jackson', id: 5 }]

预期输出:

[{ name: 'harry', id: 1 }, { name: 'john', id: 2 }, { 'name': 'trevor', id: 3 }, { name: 'ron', id : 4 }, { name: 'jackson', id: 5 }]

谢谢!!

【问题讨论】:

  • 两个不同的对象永远不会是===。对象比较只涉及对象的引用值;属性值无关紧要。
  • 您可能需要对函数 Set() 进行原型设计或向您的对象类型添加自定义比较方法,因为 @Pointy 表示对象比较是通过引用完成的,这将不相等。
  • 相关信息:How to customize object equality for Javascript Set - 剧透:“你不能”所以你不能真正使用 Set 来做你想做的事。它根本无法满足您的需求,也无法定制。
  • 你可以使用reduce,类似fullNameList.reduce((a, c) => { !a.find(v => v.id === c.id) && a.push(c); return a; }, []);
  • 您需要确定是什么使两个对象相同,并将其用于find 函数中的比较。我认为这是id 属性。

标签: javascript arrays ecmascript-6


【解决方案1】:

这不适用于对象,因为它们不是相同的对象:

{ name: 'harry', id: 1 } !== { name: 'harry', id: 1 }

一种可行的方法是:

let filteredList = [...new Set(fullNameList.map(JSON.stringify))].map(JSON.parse);

不推荐,因为它在不必要的地方使用了序列化。序列化将破坏无法序列化的对象实例,将忽略不可枚举的属性并且不会处理循环引用。具有不同属性顺序的对象不会作为重复项被删除。应该首先对它们的属性进行排序。如果存在嵌套对象,则应对键进行递归排序。这使得这种方法不切实际。

如果对象由一个或多个字段标识,则可以使用带有序列化字段作为映射键的Map

let filteredList = [
  ...new Map(fullNameList.map(obj => [`${obj.id}:${obj.name}`, obj]))
  .values()
];

【讨论】:

  • 将对象转换为字符串表示。不客气。
  • 我们不能使用对象到字符串的表示,因为键的顺序可能不同
  • @lalrishav 我同意。当我尝试在 Redis 中使用 Set 数据类型存储字符串化对象时遇到了同样的问题。仅仅因为属性的顺序不同,从 Firebase 为同一实体返回的相同数据在字符串化时不相等。
  • @AlperGüven FWIW,上次我在 Firebase 上遇到这个问题时,我使用了 json-stable-stringify
【解决方案2】:

一个很好的方法是继承Set

class UniqueNameSet extends Set {
    constructor(values) {
        super(values);

        const names = [];
        for (let value of this) {
            if (names.includes(value.name)) {
                this.delete(value);
            } else {
                names.push(value.name);
            }
        }
    }
}

这为您提供了 Set 的所有其他方法/属性,但添加了您正在寻找的额外功能。

【讨论】:

    【解决方案3】:

    每个对象都是指向内存地址的指针,因此即使两个对象具有相同的属性,它也不会相等。 一种方法是使用 JSON.stringify 并复制字符串化的对象。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-01-21
      • 2022-06-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-07-08
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多