Подтвердить что ты не робот

Сравнение наборов ECMA6 для равенства

Как вы сравниваете два набора javascript? Я пробовал использовать == и ===, но оба возвращали false.

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

Эти два набора эквивалентны, потому что по определению sets не имеют порядка (по крайней мере, обычно). Я просмотрел документацию для Set on MDN и не нашел ничего полезного. Кто-нибудь знает, как это сделать?

4b9b3361

Ответ 1

Попробуйте следующее:

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;
}

Ответ 2

Вы также можете попробовать:

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

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

console.log(isSetsEqual(a,b)) 

Ответ 3

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

Ответ 4

Другой ответ будет работать нормально; вот еще одна альтернатива.

// 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 }])

вернет false. Если вышеупомянутые два набора считаются равными, нам нужно выполнить итерацию через оба набора, выполняющих глубокие сравнения качества для каждого элемента. Мы оговариваем существование подпрограммы 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.

Ответ 5

Ни одно из этих решений не "возвращает" ожидаемую функциональность в структуру данных, такую как наборы наборов. В своем текущем состоянии набор Javascript для этой цели бесполезен, поскольку надмножество будет содержать повторяющиеся подмножества, которые Javascript ошибочно считает отличными. Единственное решение, которое я могу придумать, - это преобразовать каждое подмножество в массив, отсортировать его и затем кодировать в виде строки (например, 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>

Ответ 6

Сравнение двух объектов с ==, ===

При использовании оператора == или === для сравнения двух объектов вы всегда получите false если только эти объекты не ссылаются на один и тот же объект. Например:

var a = b = new Set([1,2,3]); // NOTE: b will become a global variable
a == b; // <-- true: a and b share the same object reference

В противном случае value == равно false, даже если объект содержит те же значения:

var a = new Set([1,2,3]);
var b = new Set([1,2,3]);
a == b; // <-- false: a and b are not referencing the same object

Возможно, вам придется рассмотреть ручное сравнение

В ECMAScript 6 вы можете заранее конвертировать массивы в массивы, чтобы вы могли определить разницу между ними:

function setsEqual(a,b){
    if (a.size !== b.size)
        return false;
    let aa = Array.from(a); 
    let bb = Array.from(b);
    return aa.filter(function(i){return bb.indexOf(i)<0}).length==0;
}

ПРИМЕЧАНИЕ. Array.from является одним из стандартных функций ECMAScript 6, но в современных браузерах он широко не поддерживается. Здесь проверьте таблицу совместимости: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/from#Browser_compatibility

Ответ 7

Я следую этому подходу в тестах:

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);

Ответ 8

Я создал быстрый polyfill для Set.prototype.isEqual()

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

Ответ 9

Исходя из принятого ответа, при условии поддержки Array.from, вот одна Array.from:

function eqSet(a, b) {
    return a.size === b.size && Array.from(a).every(b.has.bind(b));
}

Ответ 10

Причина, по которой ваш подход возвращает false, заключается в том, что вы сравниваете два разных объекта (даже если они получили одинаковое содержимое), и, таким образом, сравнение двух разных объектов (не ссылок, а объектов) всегда возвращает вас к ошибочности.

Следующий подход объединяет два набора в один и просто тупо сравнивает размер. Если это то же самое, это то же самое:

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);

Перевернутый: это очень просто и коротко. Нет внешней библиотеки, только ванильный JS

Недостаток: вероятно, это будет медленнее, чем просто перебирать значения, и вам нужно больше места.

Ответ 11

Если наборы содержат только примитивные типы данных или объекты внутри наборов имеют ссылочное равенство, то существует более простой способ

const isEqualSets = (set1, set2) => (set1.size === set2.size) && (set1.size === new Set([...set1,...set2]).size);

Ответ 12

Очень небольшая модификация, основанная на ответе @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
);

Если у кого-то еще возникла проблема, как у меня, из-за какой-то причуды из последней вавилоны, здесь нужно добавить явное условие.

(Также для множественного числа я думаю, что are немного интуитивнее читать вслух 🙃)

Ответ 13

1) Проверьте, равны ли размеры. Если нет, то они не равны.

2) итерация по каждому элементу A и проверка того, что существует в B. Если один из них возвращает return unequal

3) Если вышеприведенные 2 условия терпят неудачу, это означает, что они равны.

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));