Javascript 比较 ECMA6 集的相等性
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/31128855/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
comparing ECMA6 sets for equality
提问by williamcodes
How do you compare two javascript sets? I tried using ==
and ===
but both return false.
你如何比较两个 javascript 集?我尝试使用==
,===
但都返回 false。
a = new Set([1,2,3]);
b = new Set([1,3,2]);
a == b; //=> false
a === b; //=> false
These two sets are equivalent, because by definition, sets do not have order(at least not usually). I've looked at the documentation for Set on MDNand found nothing useful. Anyone know how to do this?
这两个集合是等价的,因为根据定义,集合没有顺序(至少通常不是)。我查看了 MDN 上 Set 的文档,但没有发现任何有用的信息。有人知道怎么做吗?
采纳答案by Aadit M Shah
Try this:
尝试这个:
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;
}
A more functional approach would be:
更实用的方法是:
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);
};
}
The all
function works for all iterable objects (e.g. Set
and Map
).
该all
函数适用于所有可迭代对象(例如Set
和Map
)。
If Array.from
was more widely supported then we could have implemented the all
function as:
如果Array.from
得到更广泛的支持,那么我们可以将all
功能实现为:
function all(pred, as) {
return Array.from(as).every(pred);
}
Hope that helps.
希望有帮助。
回答by Max Leizerovich
You can also try:
您也可以尝试:
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))
回答by anthonyserious
lodashprovides _.isEqual()
, which does deep comparisons. This is very handy if you don't want to write your own. As of lodash 4, _.isEqual()
properly compares Sets.
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
回答by anthonyserious
The other answer will work fine; here is another alternative.
另一个答案可以正常工作;这是另一种选择。
// 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); }
However, be aware that this does notdo deep equality comparison. So
但是,要知道,这并没有做深相等比较。所以
eqSet(Set([{ a: 1 }], Set([{ a: 1 }])
will return false. If the above two sets are to be considered equal, we need to iterate through both sets doing deep quality comparisons on each element. We stipulate the existence of a deepEqual
routine. Then the logic would be
将返回假。如果要认为上述两个集合相等,我们需要遍历两个集合,对每个元素进行深度质量比较。我们规定了一个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;
}
What this does: for each member of s1, look for a deeply equal member of s2. If found, delete it so it can't be used again. The two sets are deeply equal if all the elements in s1 are found in s2, ands2 is exhausted. Untested.
这是做什么的:对于 s1 的每个成员,寻找 s2 的一个非常平等的成员。如果找到,请将其删除,以免再次使用。如果 s1 中的所有元素都在 s2 中找到,并且s2 已用尽,则这两个集合深度相等。未经测试。
You may find this useful: http://www.2ality.com/2015/01/es6-set-operations.html.
您可能会发现这很有用:http: //www.2ality.com/2015/01/es6-set-operations.html。
回答by 7vujy0f0hy
None of these solutions bring “back” the expected functionality to a data structure such as set of sets.In its current state, the Javascript Setis 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(for example JSON).
这些解决方案都没有将预期的功能“恢复”到诸如集合之类的数据结构中。在其当前状态下,Javascript集对于此目的是无用的,因为超集将包含重复的子集,Javascript 错误地将其视为不同的子集。我能想到的唯一解决方案是将每个子集转换为Array,对其进行排序,然后编码为String(例如 JSON)。
Solution
解决方案
var toJsonSet = aset /* array or set */ => JSON.stringify([...new Set(aset)].sort());
var fromJsonSet = jset => new Set(JSON.parse(jset));
Basic usage
基本用法
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>
Ultimate test: set of sets
终极测试:套组
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>
回答by thadeuszlay
The reason why your approach returns false is because you are comparing two different objects (even if they got the same content), thus comparing two different objects (not references, but objects) always returns you falsy.
您的方法返回 false 的原因是因为您正在比较两个不同的对象(即使它们具有相同的内容),因此比较两个不同的对象(不是引用,而是对象)总是会返回 false。
The following approach merges two sets into one and just stupidly compares the size. If it's the same, it's the same:
以下方法将两组合并为一组,只是愚蠢地比较大小。如果相同,则相同:
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);
Upside: It's very simple and short. No external library only vanilla JS
好处:它非常简单和简短。没有外部库只有vanilla JS
Downside: It's probably going to be a slower than just iterating over the values and you need more space.
缺点:它可能比仅迭代值要慢,并且您需要更多空间。
回答by TaoPR
Comparing two objects with ==, ===
用 ==, === 比较两个对象
When using ==
or ===
operator to compare two objects, you will always get false
unless those object reference the same object. For example:
使用==
or===
运算符比较两个对象时,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
Otherwise, == equates to false even though the object contains the same values:
否则,即使对象包含相同的值, == 也等于 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
You may need to consider manual comparison
您可能需要考虑手动比较
In ECMAScript 6, you may convert sets to arrays beforehand so you can spot the difference between them:
在 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;
}
NOTE:Array.from
is one of the standard ECMAScript 6 features but it is not widely supported in modern browsers. Check the compatibility table here : https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/from#Browser_compatibility
注意:Array.from
是 ECMAScript 6 的标准特性之一,但在现代浏览器中并未得到广泛支持。在此处检查兼容性表:https: //developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/from#Browser_compatibility
回答by Lenny
I created a quick polyfill for Set.prototype.isEqual()
我为 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;
}
回答by John Hoffer
Based on the accepted answer, assuming support of Array.from
, here is a one-liner:
根据接受的答案,假设支持Array.from
,这里是一个单行:
function eqSet(a, b) {
return a.size === b.size && Array.from(a).every(b.has.bind(b));
}
回答by Mikhail Unenov
If sets contains only primitive data types or object inside sets have reference equality, then there is simpler way
如果集合只包含原始数据类型或集合内的对象具有引用相等性,则有更简单的方法
const isEqualSets = (set1, set2) => (set1.size === set2.size) && (set1.size === new Set([...set1, ...set2]).size);
const isEqualSets = (set1, set2) => (set1.size === set2.size) && (set1.size === new Set([...set1, ...set2]).size);