我正在尝试通过lib.es6.d.ts使用TypeScript中的ES6。我需要了解enforce equality comparison and sameness如何在Set<T>
中使用对象link on MDN。例如,对象如下所示。
class Product {
metadata: Map<String, String>
constructor(id: number) {
metadata = new Map<String, String>();
}
public addMetadata(key: String, val: String): Product {
this.metadata.set(key, val);
return this;
}
}
请注意id
中的Product
字段值决定了它的唯一性。如果两个Product实例具有相同的ID,则在我的应用程序中它们被认为是相同的,即使metadata
不同。一般来说,我只希望将一部分字段用作测试相等性和相同性的一部分。
在Java中,我们覆盖equals
方法来控制和测试相同性。在JavaScript中,我们需要做些什么来确定相同性?
MDN shows 4 equality algorithms声明如下:
因为Set中的每个值都必须是唯一的,所以将检查值相等。
基本上,在上面的课程中,我想做类似以下的事情。
let p1 = new Product(1);
let p2 = new Product(2);
let p3 = new Product(1); //duplicate of p1 by id
p1.addMetadata('k1','v1').addMetadata('k2','v2');
p2.addMetadata('k1','v1');
let mySet = new Set<Product>();
mySet.add(p1);
mySet.add(p2);
mySet.add(p3);
assertEquals(2, mySet.size); //some assertion method
答案 0 :(得分:2)
检查对象是否相等的简单(也是高效)方法是比较JSON.stringify()
个字符串。由于只有自己的可枚举属性被字符串化,metadata
属性应该是不可枚举的:
class Product {
metadata: Map<String, String>
constructor(public id: number) {
Object.defineProperty(this, 'metadata', {
configurable: true,
writable: true
})
this.metadata = new Map<String, String>();
}
...
}
这将导致:
new Product(1) !== new Product(1);
JSON.stringify(new Product(1)) === JSON.stringify(new Product(1));
JSON.stringify(new Product(1)) !== JSON.stringify(new Product(2));
此方法可用于自定义Set
:
class ObjectedSet extends Set {
protected _find(searchedValue) {
for (const value of Array.from(this.values()))
if (JSON.stringify(value) === JSON.stringify(searchedValue))
return searchedValue;
}
has(value) {
return !!this._find(value);
}
add(value) {
if (!this.has(value))
super.add(value);
return this;
}
delete(value) {
const foundValue = this._find(value);
if (foundValue)
super.delete(foundValue);
return !!foundValue;
}
}
答案 1 :(得分:1)
Set
或Map
认为只有当对象完全是同一个对象而不是具有相同内容但具有相同实际对象的不同对象时,该对象才是相同的。换句话说,它就像obj1 === obj2
一样。
如果你使用Set
喜欢你的标题和第一段引用,那么你几乎没有运气。两个单独的对象,相同的内容(或在您的情况下,相同的.id
属性)将被视为Set
中的单独项目,因为它们实际上是不同的对象。
您可以在此问题How to customize object equality for JavaScript Set中看到关于这是否可以自定义Set
(不是)的讨论。
如果您使用的是Map
(这似乎是您的代码所指的内容,即使这不是您的问题文本所说的内容),那么您可以使用.id
属性作为键和对象本身作为值。只要.id
属性是基元(如字符串或数字),那么对于任何给定的Map
,您只会在id
中获得一个项目。
答案 2 :(得分:1)
不直接回答您的问题,但“标记集”(缺少更好的名称)可能比重写的相等运算符更好。这样,“相等”是集合本身的属性,而不是底层对象的属性。这是一个JS示例:
class SetBy extends Set {
constructor(pred) {
super();
this.pred = pred;
this.inner = new Set();
}
has(obj) {
return this.inner.has(this.pred(obj));
}
add(obj) {
if (!this.has(obj)) {
this.inner.add(this.pred(obj));
super.add(obj);
}
}
}
s = new SetBy(x => x.id);
a = {id: 1, name: 'a'};
b = {id: 1, name: 'b'};
c = {id: 1, name: 'c'};
d = {id: 2, name: 'd'};
e = {id: 2, name: 'e'};
s.add(a);
s.add(b);
s.add(c);
s.add(d);
s.add(e);
console.log([...s]);