比较ECMA6集合的相等性

211

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

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

这两组是等价的,因为根据定义,集合没有顺序(至少通常情况下是这样)。我查看了MDN 上 Set 的文档,但没有找到有用的信息。有人知道怎么做吗?


那么你如何比较它们呢? - williamcodes
3
迭代并比较每个成员的值,如果全部相同,则集合为“相同”。 - dandavis
1
@dandavis,使用集合时,成员就是值。 - user663031
4
集合(Set)和映射(Map)确实有一个顺序,那就是插入的顺序 - 无论出于什么原因: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set/entries - CodeManX
23
最糟糕的是,即使是new Set([1,2,3]) != new Set([1,2,3])。这使得JavaScript中的 Set 对于“集合的集合”而言毫无用处,因为超级集合将包含重复的子集。唯一想到的解决方法是将所有子集转换为数组,对每个数组进行排序,然后将每个数组编码为字符串(例如JSON)。 - 7vujy0f0hy
显示剩余3条评论
18个回答

2
你的方法返回 false 的原因是因为你比较了两个不同的对象(即使它们具有相同的内容),因此比较两个不同的对象(而不是引用,而是对象)总是会返回 falsy。
以下方法将两个集合合并成一个,并愚蠢地比较其大小。如果相同,则它们是相同的:
const a1 = [1,2,3];
const a2 = [1,3,2];
const set1 = new Set(a1);
const set2 = new Set(a2);

const compareSet = new Set([...a1, ...a2]);
const isSetEqual = compareSet.size === set2.size && compareSet.size === set1.size;
console.log(isSetEqual);

优点: 它非常简单和短小。没有外部库,只使用vanilla JS。

缺点: 可能比仅迭代值要慢,并且需要更多的空间。


2

基于@Aadit M Shah的答案做了非常小的修改:

/**
 * check if two sets are equal in the sense that
 * they have a matching set of values.
 *
 * @param {Set} a 
 * @param {Set} b
 * @returns {Boolean} 
 */
const areSetsEqual = (a, b) => (
        (a.size === b.size) ? 
        [...a].every( value => b.has(value) ) : false
);

如果还有其他人像我一样因为最新版本的babel出现了一些怪异问题,那么就必须在这里添加一个明确的条件语句来解决问题。

(此外,我认为对于复数时,使用are更容易口头阅读。)


2

我为Set.prototype.isEqual()创建了一个快速的polyfill。

Set.prototype.isEqual = function(otherSet) {
    if(this.size !== otherSet.size) return false;
    for(let item of this) if(!otherSet.has(item)) return false;
    return true;
}

Github Gist - Set.prototype.isEqual

{{链接1:Github Gist - Set.prototype.isEqual}}


1

使用 Ramda : equals(set1, set2)

const s1 = new Set([1, 2, 3]);
const s2 = new Set([3, 1, 2]);

console.log( R.equals(s1, s2) );
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.27.1/ramda.min.js"></script>


0

现有的答案都没有检查Set的插入顺序,所以这里提供一个可以做到的方法。它使用lodash的_.isEqualWith进行浅层次检查(因为_.isEqual会进行深度检查,而且对于Set来说速度较慢)。

import isEqualWith from 'lodash/isEqualWith';

export function setsAreEqual (setA: Set<unknown>, setB: Set<unknown>): boolean {
    return isEqualWith(setA, setB, (a: unknown, b: unknown) => {
        if (a === setA) return undefined;
        return a === b;
    });
}

如果你在意顺序,那么你可能对集合的使用有些误解。 - undefined
存在不同的意见。特别是,设计Set以允许有序检索的规范的作者们持有不同的观点。 - undefined

0
我在测试中遵循这种方法:

let setA = new Set(arrayA);
let setB = new Set(arrayB);
let diff = new Set([...setA].filter(x => !setB.has(x)));
expect([...diff].length).toBe(0);

5
等一下……这只检查A是否有B中没有的元素吗?它不检查B是否有A中没有的元素。如果你尝试a=[1,2,3]b=[1,2,3,4],它会说它们是相同的。所以我猜你需要额外的检查,例如setA.size === setB.size - user993683

0
如果您想允许使用自定义比较器选项而不是 ===
function setEquals(x, y, comparer) { 
  if (!comparer) {
    return x.size === y.size
      && [...x].every(a => y.has(a));
  }

  return x.size === y.size
    && [...x].every(a => [...y].some(b => comparer(a, b)));
}

如果您不传递比较器,这仍将与其他方法相同,但您可以选择传递自定义比较方法,例如 (a, b) => a.id === b.id


-1

1)检查尺寸是否相等。如果不相等,则它们不相等。

2)迭代A的每个元素并检查是否存在于B中。如果有一个失败,则返回unequal

3)如果上述两个条件失败,那么它们相等。

let isEql = (setA, setB) => {
  if (setA.size !== setB.size)
    return false;
  
  setA.forEach((val) => {
    if (!setB.has(val))
      return false;
  });
  return true;
}

let setA = new Set([1, 2, {
  3: 4
}]);
let setB = new Set([2, {
    3: 4
  },
  1
]);

console.log(isEql(setA, setB));

2) 方法二

let isEql = (A, B) => {
  return JSON.stringify([...A].sort()) == JSON.stringify([...B].sort());
}

let res = isEql(new Set([1, 2, {3:4}]), new Set([{3:4},1, 2]));
console.log(res);


这个答案完全是错误的。forEach方法中的返回语句不会使父函数返回。 - xaviert

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接