如何为JavaScript Set自定义对象相等性

2020/10/24 11:21 · javascript ·  · 0评论

新的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()方法只关闭它是一个基本相同的实际物体或相同的值,没有办法插头插入或更换只是逻辑。

你大概可以从派生您自己的对象Set和替换.has().add().delete()与一些做了深刻的对象比较方法,先找到,如果该项目已经在设置,但性能可能不会很好,因为底层Set对象不会帮助完全没有 在调用原始对象之前,您可能只需要对所有现有对象进行蛮力迭代才能找到匹配项.add()

以下是本文的一些信息以及对ES6功能的讨论

5.2为什么我不能配置映射和设置比较键和值的方式?

问题:如果有一种方法可以配置哪些映射键和哪些设置元素被认为是相等的,那就太好了。为什么不在那里?

答:由于难以正确有效地实施,该功能已被推迟。一种选择是将回调传递给指定相等性的集合。

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包装器,该包装器接受了自定义哈希函数,自定义相等函数,并在存储桶中存储了具有等效(自定义)哈希值的不同值。

可以预见的是,它竟然是慢切尔尼的字符串连接方法

全文在这里:https : //github.com/makoConstruct/ValueMap

直接比较它们似乎是不可能的,但是如果仅对键进行排序,则JSON.stringify可以工作。正如我在评论中指出的

JSON.stringify({a:1,b:2})!== JSON.stringify({b:2,a:1});

但是我们可以使用自定义的stringify方法解决该问题。首先我们写方法

自定义Stringify

Object.prototype.stringifySorted = function(){
    let oldObj = this;
    let obj = (oldObj.length || oldObj.length === 0) ? [] : {};
    for (let key of Object.keys(this).sort((a, b) => a.localeCompare(b))) {
        let type = typeof (oldObj[key])
        if (type === 'object') {
            obj[key] = oldObj[key].stringifySorted();
        } else {
            obj[key] = oldObj[key];
        }
    }
    return JSON.stringify(obj);
}

集合

现在我们使用一个集合。但是我们使用一组字符串代替对象

let set = new Set()
set.add({a:1, b:2}.stringifySorted());

set.has({b:2, a:1}.stringifySorted());
// returns true

获取所有值

创建集合并添加值之后,我们可以通过

let iterator = set.values();
let done = false;
while (!done) {
  let val = iterator.next();

  if (!done) {
    console.log(val.value);
  }
  done = val.done;
}

这是一个包含所有文件的链接
http://tpcg.io/FnJg2i

也许您可以尝试使用它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);

对于Typescript用户,其他人(尤其是czerny的答案可以概括为一个很好的类型安全和可重用的基类:

/**
 * Map that stringifies the key objects in order to leverage
 * the javascript native Map and preserve key uniqueness.
 */
abstract class StringifyingMap<K, V> {
    private map = new Map<string, V>();
    private keyMap = new Map<string, K>();

    has(key: K): boolean {
        let keyString = this.stringifyKey(key);
        return this.map.has(keyString);
    }
    get(key: K): V {
        let keyString = this.stringifyKey(key);
        return this.map.get(keyString);
    }
    set(key: K, value: V): StringifyingMap<K, V> {
        let keyString = this.stringifyKey(key);
        this.map.set(keyString, value);
        this.keyMap.set(keyString, key);
        return this;
    }

    /**
     * Puts new key/value if key is absent.
     * @param key key
     * @param defaultValue default value factory
     */
    putIfAbsent(key: K, defaultValue: () => V): boolean {
        if (!this.has(key)) {
            let value = defaultValue();
            this.set(key, value);
            return true;
        }
        return false;
    }

    keys(): IterableIterator<K> {
        return this.keyMap.values();
    }

    keyList(): K[] {
        return [...this.keys()];
    }

    delete(key: K): boolean {
        let keyString = this.stringifyKey(key);
        let flag = this.map.delete(keyString);
        this.keyMap.delete(keyString);
        return flag;
    }

    clear(): void {
        this.map.clear();
        this.keyMap.clear();
    }

    size(): number {
        return this.map.size;
    }

    /**
     * Turns the `key` object to a primitive `string` for the underlying `Map`
     * @param key key to be stringified
     */
    protected abstract stringifyKey(key: K): string;
}

这样的示例实现就这么简单:只需重写该stringifyKey方法即可。就我而言,我将一些uri财产归类

class MyMap extends StringifyingMap<MyKey, MyValue> {
    protected stringifyKey(key: MyKey): string {
        return key.uri.toString();
    }
}

这样,示例用法就好像是常规的Map<K, V>

const key1 = new MyKey(1);
const value1 = new MyValue(1);
const value2 = new MyValue(2);

const myMap = new MyMap();
myMap.set(key1, value1);
myMap.set(key1, value2); // native Map would put another key/value pair

myMap.size(); // returns 1, not 2

对于某个在Google上(和我一样)想找到此问题的人,想要使用对象作为Key来获取Map的值:

警告:此答案不适用于所有对象

var map = new Map<string,string>();

map.set(JSON.stringify({"A":2} /*string of object as key*/), "Worked");

console.log(map.get(JSON.stringify({"A":2}))||"Not worked");

输出:

工作了

从两个集合的组合中创建一个新集合,然后比较长度。

let set1 = new Set([1, 2, 'a', 'b'])
let set2 = new Set([1, 'a', 'a', 2, 'b'])
let set4 = new Set([1, 2, 'a'])

function areSetsEqual(set1, set2) {
  const set3 = new Set([...set1], [...set2])
  return set3.size === set1.size && set3.size === set2.size
}

console.log('set1 equals set2 =', areSetsEqual(set1, set2))
console.log('set1 equals set4 =', areSetsEqual(set1, set4))

set1等于set2 = true

set1等于set4 = false

本文地址:http://javascript.askforanswer.com/ruheweijavascript-setzidingyiduixiangxiangdengxing.html
文章标签: ,   ,  
版权声明:本文为原创文章,版权归 javascript 所有,欢迎分享本文,转载请保留出处!

文件下载

老薛主机终身7折优惠码boke112

上一篇:
下一篇:

评论已关闭!