比较 ECMA6 集是否相等
comparing ECMA6 sets for equality
你如何比较两个 javascript 集合?我尝试使用 ==
和 ===
但两者都是 return false.
a = new Set([1,2,3]);
b = new Set([1,3,2]);
a == b; //=> false
a === b; //=> false
这两组是等价的,因为根据定义,sets do not have order (at least not usually). I've looked at the documentation for Set on MDN并没有发现任何有用的东西。有人知道怎么做吗?
试试这个:
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
函数适用于所有可迭代对象(例如 Set
和 Map
)。
如果 Array.from
得到更广泛的支持,那么我们可以将 all
函数实现为:
function all(pred, as) {
return Array.from(as).every(pred);
}
希望对您有所帮助。
用==、===比较两个对象
当使用==
或===
运算符比较两个对象时,你总是会得到false
unless those object reference the same object。例如:
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
否则,即使对象包含相同的值,== 也等于 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 功能之一,但现代浏览器并未广泛支持它。在此处检查兼容性 table:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/from#Browser_compatibility
其他答案也可以;这是另一种选择。
// 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 }])
将 return 错误。如果要将以上两组视为相等,我们需要遍历这两组,对每个元素进行深度质量比较。我们规定存在 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 的深度相等的成员。如果找到,请将其删除,以免再次使用。如果在s2中找到了s1中的所有元素,则这两个集合深度相等,and s2被穷尽。未经测试。
您可能会发现这很有用:http://www.2ality.com/2015/01/es6-set-operations.html。
lodash 提供了 _.isEqual()
,可以进行深度比较。如果您不想自己编写,这将非常方便。从 lodash 4 开始,_.isEqual()
正确比较集合。
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
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));
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);
我在测试中遵循这种方法:
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);
None 这些解决方案将预期的功能“带回”到数据结构中,例如 set of sets。 在当前状态下,Javascript Set is useless for this purpose because the superset will contain duplicate subsets, which Javascript wrongly sees as distinct. The only solution I can think of is converting each subset to Array, sorting it and then encoding as 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>
您也可以试试:
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))
我为 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;
}
基于已接受的答案,假设支持 Array.from
,这里是一个单行:
function eqSet(a, b) {
return a.size === b.size && Array.from(a).every(b.has.bind(b));
}
如果集合仅包含原始数据类型或集合内的对象具有引用相等性,则有更简单的方法
const isEqualSets = (set1, set2) => (set1.size === set2.size) && (set1.size === new Set([...set1, ...set2]).size);
根据@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
只是大声朗读更直观一点)
你的方法returns错误的原因是因为你在比较两个不同的对象(即使它们得到相同的内容),因此总是比较两个不同的对象(不是引用,而是对象)returns你个骗子。
下面的做法是把两组合二为一,傻傻地比较大小。如果相同,则相同:
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
缺点:它可能比仅仅迭代值更慢,您需要更多 space.
可能有点晚了,但我通常会这样做:
const a = new Set([1,2,3]);
const b = new Set([1,3,2]);
// option 1
console.log(a.size === b.size && new Set([...a, ...b]).size === a.size)
// option 2
console.log([...a].sort().join() === [...b].sort().join())
与 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>
你如何比较两个 javascript 集合?我尝试使用 ==
和 ===
但两者都是 return false.
a = new Set([1,2,3]);
b = new Set([1,3,2]);
a == b; //=> false
a === b; //=> false
这两组是等价的,因为根据定义,sets do not have order (at least not usually). I've looked at the documentation for Set on MDN并没有发现任何有用的东西。有人知道怎么做吗?
试试这个:
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
函数适用于所有可迭代对象(例如 Set
和 Map
)。
如果 Array.from
得到更广泛的支持,那么我们可以将 all
函数实现为:
function all(pred, as) {
return Array.from(as).every(pred);
}
希望对您有所帮助。
用==、===比较两个对象
当使用==
或===
运算符比较两个对象时,你总是会得到false
unless those object reference the same object。例如:
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
否则,即使对象包含相同的值,== 也等于 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 功能之一,但现代浏览器并未广泛支持它。在此处检查兼容性 table:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/from#Browser_compatibility
其他答案也可以;这是另一种选择。
// 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 }])
将 return 错误。如果要将以上两组视为相等,我们需要遍历这两组,对每个元素进行深度质量比较。我们规定存在 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 的深度相等的成员。如果找到,请将其删除,以免再次使用。如果在s2中找到了s1中的所有元素,则这两个集合深度相等,and s2被穷尽。未经测试。
您可能会发现这很有用:http://www.2ality.com/2015/01/es6-set-operations.html。
lodash 提供了 _.isEqual()
,可以进行深度比较。如果您不想自己编写,这将非常方便。从 lodash 4 开始,_.isEqual()
正确比较集合。
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
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));
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);
我在测试中遵循这种方法:
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);
None 这些解决方案将预期的功能“带回”到数据结构中,例如 set of sets。 在当前状态下,Javascript Set is useless for this purpose because the superset will contain duplicate subsets, which Javascript wrongly sees as distinct. The only solution I can think of is converting each subset to Array, sorting it and then encoding as 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>
您也可以试试:
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))
我为 Set.prototype.isEqual()
创建了一个快速的 polyfillSet.prototype.isEqual = function(otherSet) {
if(this.size !== otherSet.size) return false;
for(let item of this) if(!otherSet.has(item)) return false;
return true;
}
基于已接受的答案,假设支持 Array.from
,这里是一个单行:
function eqSet(a, b) {
return a.size === b.size && Array.from(a).every(b.has.bind(b));
}
如果集合仅包含原始数据类型或集合内的对象具有引用相等性,则有更简单的方法
const isEqualSets = (set1, set2) => (set1.size === set2.size) && (set1.size === new Set([...set1, ...set2]).size);
根据@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
只是大声朗读更直观一点)
你的方法returns错误的原因是因为你在比较两个不同的对象(即使它们得到相同的内容),因此总是比较两个不同的对象(不是引用,而是对象)returns你个骗子。
下面的做法是把两组合二为一,傻傻地比较大小。如果相同,则相同:
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
缺点:它可能比仅仅迭代值更慢,您需要更多 space.
可能有点晚了,但我通常会这样做:
const a = new Set([1,2,3]);
const b = new Set([1,3,2]);
// option 1
console.log(a.size === b.size && new Set([...a, ...b]).size === a.size)
// option 2
console.log([...a].sort().join() === [...b].sort().join())
与 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>