新的 ES 6 (Harmony) 引入了新的Set对象。Set 使用的标识算法类似于===
运算符,因此不太适合比较对象:
var set = new Set();
set.add({a:1});
set.add({a:1});
console.log([...set.values()]); // Array [ Object, Object ]
如何自定义 Set 对象的相等性以进行深度对象比较?有没有类似 Java 的东西equals(Object)
?
新的 ES 6 (Harmony) 引入了新的Set对象。Set 使用的标识算法类似于===
运算符,因此不太适合比较对象:
var set = new Set();
set.add({a:1});
set.add({a:1});
console.log([...set.values()]); // Array [ Object, Object ]
如何自定义 Set 对象的相等性以进行深度对象比较?有没有类似 Java 的东西equals(Object)
?
ES6Set
对象没有任何比较方法或自定义比较可扩展性。
的.has()
,.add()
而.delete()
方法只关闭它是一个基本相同的实际物体或相同的值,没有办法插头插入或更换只是逻辑。
您大概可以从 a 派生自己的对象Set
并替换.has()
,.add()
和.delete()
首先进行深度对象比较的方法,以查找该项目是否已经在 Set 中,但性能可能不会很好,因为底层Set
对象没有帮助根本。在调用原始.add()
.
5.2 为什么我不能配置maps和sets如何比较keys和values?
问题:如果有一种方法可以配置哪些映射键和哪些集合元素被认为是相等的,那就太好了。为什么没有?
答:该功能已被推迟,因为很难正确有效地实施。一种选择是将回调传递给指定相等的集合。
在 Java 中可用的另一个选项是通过对象实现的方法(Java 中的 equals())指定相等性。然而,这种方法对于可变对象是有问题的:通常,如果一个对象发生变化,它在集合中的“位置”也必须发生变化。但这不是 Java 中发生的事情。JavaScript 可能会走更安全的路线,只为特殊的不可变对象(所谓的值对象)启用按值比较。按值比较意味着如果两个值的内容相等,则认为它们相等。原始值在 JavaScript 中按值进行比较。
正如jfriend00 的回答中提到的,等式关系的定制可能是不可能的。
以下代码概述了计算效率高(但内存昂贵)的解决方法:
class GeneralSet {
constructor() {
this.map = new Map();
this[Symbol.iterator] = this.values;
}
add(item) {
this.map.set(item.toIdString(), item);
}
values() {
return this.map.values();
}
delete(item) {
return this.map.delete(item.toIdString());
}
// ...
}
每个插入的元素都必须实现toIdString()
返回字符串的方法。当且仅当它们的toIdString
方法返回相同的值时,两个对象才被认为是相等的。
正如顶级答案所提到的,对于可变对象,自定义相等性是有问题的。好消息是(我很惊讶还没有人提到这一点)有一个非常流行的库,称为immutable-js,它提供了一组丰富的不可变类型,这些类型提供了您正在寻找的深层值相等语义。
这是您使用immutable-js 的示例:
const { Map, Set } = require('immutable');
var set = new Set();
set = set.add(Map({a:1}));
set = set.add(Map({a:1}));
console.log([...set.values()]); // [Map {"a" => 1}]
为了补充这里的答案,我继续实现了一个 Map 包装器,它采用自定义哈希函数、自定义相等函数,并在存储桶中存储具有等效(自定义)哈希值的不同值。
可以预见,结果证明它比czerny 的字符串连接方法慢。
也许您可以尝试使用JSON.stringify()
进行深度对象比较。
例如 :
const arr = [
{name:'a', value:10},
{name:'a', value:20},
{name:'a', value:20},
{name:'b', value:30},
{name:'b', value:40},
{name:'b', value:40}
];
const names = new Set();
const result = arr.filter(item => !names.has(JSON.stringify(item)) ? names.add(JSON.stringify(item)) : false);
console.log(result);