Сравнение наборов 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 и не нашел ничего полезного. Кто-нибудь знает, как это сделать?
Ответы
Ответ 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));