如何针对ES6的Set集合的使用检查类实例的相等性和相同性?

时间:2016-12-15 05:10:47

标签: javascript typescript ecmascript-6 set typescript-typings

我正在尝试通过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中的每个值都必须是唯一的,所以将检查值相等。

我认为价值平等意味着===?仅在ES2015中再次Results

基本上,在上面的课程中,我想做类似以下的事情。

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

3 个答案:

答案 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)

SetMap认为只有当对象完全是同一个对象而不是具有相同内容但具有相同实际对象的不同对象时,该对象才是相同的。换句话说,它就像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]);