比较 ECMA6 集的相等性

IT技术 javascript set ecmascript-6
2021-02-18 07:52:42

你如何比较两个 javascript 集?我尝试使用=====但都返回 false。

a = new Set([1,2,3]);
b = new Set([1,3,2]);
a == b; //=> false
a === b; //=> false

这两个集合是等价的,因为根据定义,集合没有顺序(至少通常不是)。我查看了 MDN 上的 Set 文档,但没有发现任何有用的信息。有人知道怎么做吗?

6个回答

试试这个:

var a = new Set([1,2,3]);
var b = new Set([1,3,2]);

alert(eqSet(a, b)); // true

function eqSet(as, bs) {
    if (as.size !== bs.size) return false;
    for (var a of as) if (!bs.has(a)) return false;
    return true;
}

更实用的方法是:

var a = new Set([1,2,3]);
var b = new Set([1,3,2]);

alert(eqSet(a, b)); // true

function eqSet(as, bs) {
    return as.size === bs.size && all(isIn(bs), as);
}

function all(pred, as) {
    for (var a of as) if (!pred(a)) return false;
    return true;
}

function isIn(as) {
    return function (a) {
        return as.has(a);
    };
}

all函数适用于所有可迭代对象(例如SetMap)。

如果Array.from得到更广泛的支持,那么我们可以将all功能实现为:

function all(pred, as) {
    return Array.from(as).every(pred);
}

希望有帮助。

我想你应该改变名称has,以isPartOfisInelem
2021-04-24 07:52:42
@DavidGiven 是的,JavaScript 中的集合按插入顺序迭代:developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/...
2021-04-28 07:52:42
@Bergi 我将函数的名称更改hasisIn.
2021-05-06 07:52:42
@GhasanAl-Sakkaf,我同意,可能 TC39 由科学家组成,但没有实用主义者......
2021-05-07 07:52:42
每天我都更加确信 JavaScript 是有史以来最糟糕的语言。每个人都必须发明自己的基本功能来应对其局限性,这在 ES6 中,而在 2017 年中!为什么他们不能在 Set 对象的规范中添加如此频繁的功能!
2021-05-08 07:52:42

你也可以试试:

var a = new Set([1,2,3]);
var b = new Set([1,3,2]);

let areSetsEqual = (a, b) => a.size === b.size && [...a].every(value => b.has(value));

console.log(areSetsEqual(a,b)) 

绝对是一个更好的解决方案,因为它适合 if 条件
2021-04-17 07:52:42
我喜欢这个解决方案是多么地道,多么易读!谢谢@Max
2021-04-30 07:52:42
谢谢你让我发现Array.every但是因为它不修改a,为什么你需要使用[...a],制作副本?
2021-05-07 07:52:42
every() 是数组 API 的一部分,它不存在于 Set 或 Iterator
2021-05-09 07:52:42

lodash提供_.isEqual(),它进行深度比较。如果您不想自己编写,这非常方便。从 lodash 4 开始,_.isEqual()正确比较 Sets。

const _ = require("lodash");

let s1 = new Set([1,2,3]);
let s2 = new Set([1,2,3]);
let s3 = new Set([2,3,4]);

console.log(_.isEqual(s1, s2)); // true
console.log(_.isEqual(s1, s3)); // false

这些解决方案都没有将预期的功能“恢复”到数据结构(例如集合集)中。在其当前状态下,Javascript对于此目的是无用的,因为超集将包含重复的子集,Javascript 错误地将其视为不同的。我能想到的唯一解决方案是将每个子集转换为Array,对其进行排序,然后编码为String(例如 JSON)。

解决方案

var toJsonSet = aset /* array or set */ => JSON.stringify([...new Set(aset)].sort()); 
var fromJsonSet = jset => new Set(JSON.parse(jset));

基本用法

var toJsonSet = aset /* array or set */ => JSON.stringify([...new Set(aset)].sort()); 
var fromJsonSet = jset => new Set(JSON.parse(jset));

var [s1,s2] = [new Set([1,2,3]), new Set([3,2,1])];
var [js1,js2] = [toJsonSet([1,2,3]), toJsonSet([3,2,1])]; // even better

var r = document.querySelectorAll("td:nth-child(2)");
r[0].innerHTML = (toJsonSet(s1) === toJsonSet(s2)); // true
r[1].innerHTML = (toJsonSet(s1) == toJsonSet(s2)); // true, too
r[2].innerHTML = (js1 === js2); // true
r[3].innerHTML = (js1 == js2); // true, too

// Make it normal Set:
console.log(fromJsonSet(js1), fromJsonSet(js2)); // type is Set
<style>td:nth-child(2) {color: red;}</style>

<table>
<tr><td>toJsonSet(s1) === toJsonSet(s2)</td><td>...</td></tr>
<tr><td>toJsonSet(s1) == toJsonSet(s2)</td><td>...</td></tr>
<tr><td>js1 === js2</td><td>...</td></tr>
<tr><td>js1 == js2</td><td>...</td></tr>
</table>

终极测试:套组

var toSet = arr => new Set(arr);
var toJsonSet = aset /* array or set */ => JSON.stringify([...new Set(aset)].sort()); 
var toJsonSet_WRONG = set => JSON.stringify([...set]); // no sorting!

var output = document.getElementsByTagName("code"); 
var superarray = [[1,2,3],[1,2,3],[3,2,1],[3,6,2],[4,5,6]];
var superset;

Experiment1:
    superset = toSet(superarray.map(toSet));
    output[0].innerHTML = superset.size; // incorrect: 5 unique subsets
Experiment2:
    superset = toSet([...superset].map(toJsonSet_WRONG));
    output[1].innerHTML = superset.size; // incorrect: 4 unique subsets
Experiment3:
    superset = toSet([...superset].map(toJsonSet));
    output[2].innerHTML = superset.size; // 3 unique subsets
Experiment4:
    superset = toSet(superarray.map(toJsonSet));
    output[3].innerHTML = superset.size; // 3 unique subsets
code {border: 1px solid #88f; background-color: #ddf; padding: 0 0.5em;}
<h3>Experiment 1</h3><p>Superset contains 3 unique subsets but Javascript sees <code>...</code>.<br>Let’s fix this... I’ll encode each subset as a string.</p>
<h3>Experiment 2</h3><p>Now Javascript sees <code>...</code> unique subsets.<br>Better! But still not perfect.<br>That’s because we didn’t sort each subset.<br>Let’s sort it out...</p>
<h3>Experiment 3</h3><p>Now Javascript sees <code>...</code> unique subsets. At long last!<br>Let’s try everything again from the beginning.</p>
<h3>Experiment 4</h3><p>Superset contains 3 unique subsets and Javascript sees <code>...</code>.<br><b>Bravo!</b></p>

不幸的是,我现在没有时间回顾这个,但是大多数使用内置默认值对 js 集合的键进行排序的解决方案.sort()都是错误的,因为 js 对象没有总顺序,例如 NaN!=NaN, '2'< 3(胁迫)等。
2021-04-26 07:52:42
很棒的解决方案!如果你知道你只有一组字符串或数字,它就会变成[...set1].sort().toString() === [...set2].sort().toString()
2021-05-02 07:52:42

另一个答案可以正常工作;这是另一种选择。

// Create function to check if an element is in a specified set.
function isIn(s)          { return elt => s.has(elt); }

// Check if one set contains another (all members of s2 are in s1).
function contains(s1, s2) { return [...s2] . every(isIn(s1)); }

// Set equality: a contains b, and b contains a
function eqSet(a, b)      { return contains(a, b) && contains(b, a); }

// Alternative, check size first
function eqSet(a, b)      { return a.size === b.size && contains(a, b); }

但是,要知道,这并没有做深相等比较。所以

eqSet(Set([{ a: 1 }], Set([{ a: 1 }])

将返回假。如果要认为上述两个集合相等,我们需要遍历两个集合,对每个元素进行深度质量比较。我们规定了一个deepEqual例程的存在那么逻辑将是

// Find a member in "s" deeply equal to some value
function findDeepEqual(s, v) { return [...s] . find(m => deepEqual(v, m)); }

// See if sets s1 and s1 are deeply equal. DESTROYS s2.
function eqSetDeep(s1, s2) {
  return [...s1] . every(a1 => {
    var m1 = findDeepEqual(s2, a1);
    if (m1) { s2.delete(m1); return true; }
  }) && !s2.size;
}

这是做什么的:对于 s1 的每个成员,寻找 s2 的一个非常平等的成员。如果找到,请将其删除,以免再次使用。如果 s1 中的所有元素都在 s2 中找到,并且s2 已用尽,则这两个集合非常相等未经测试。

您可能会发现这很有用:http : //www.2ality.com/2015/01/es6-set-operations.html