var a = [1, 2, 3];
var b = [3, 2, 1];
var c = new Array(1, 2, 3);
alert(a == b + "|" + b == c);
如何检查这些数组是否相等并获得一个方法,true
如果它们相等则返回?
jQuery 是否为此提供任何方法?
var a = [1, 2, 3];
var b = [3, 2, 1];
var c = new Array(1, 2, 3);
alert(a == b + "|" + b == c);
如何检查这些数组是否相等并获得一个方法,true
如果它们相等则返回?
jQuery 是否为此提供任何方法?
这是你应该做的。请不要使用stringify
或< >
。
function arraysEqual(a, b) {
if (a === b) return true;
if (a == null || b == null) return false;
if (a.length !== b.length) return false;
// If you don't care about the order of the elements inside
// the array, you should sort both arrays here.
// Please note that calling sort on an array will modify that array.
// you might want to clone your array first.
for (var i = 0; i < a.length; ++i) {
if (a[i] !== b[i]) return false;
}
return true;
}
[2021 更新日志:选项 4 的错误修正:js 对象没有总排序(甚至不包括NaN!=NaN
和'5'==5
( '5'===5
、'2'<3
等)),所以不能.sort(cmpFunc)
在 Map.keys() 上使用(尽管你可以使用Object.keys(obj)
,因为即使是“数字”键也是字符串) .]
选项1
最简单的选项,几乎适用于所有情况,除了null
!==undefined
但它们都被转换为 JSON 表示null
并被认为是相等的:
function arraysEqual(a1,a2) {
/* WARNING: arrays must not contain {objects} or behavior may be undefined */
return JSON.stringify(a1)==JSON.stringify(a2);
}
(如果您的数组包含对象,这可能不起作用。这是否仍然适用于对象取决于 JSON 实现是否对键进行排序。例如, JSON{1:2,3:4}
可能等于或不等于{3:4,1:2}
; 这取决于实现,规范不做任何保证。[2017 年更新:实际上,ES6 规范现在保证对象键将按照 1) 整数属性,2) 属性按定义顺序迭代,然后 3) 符号属性按定义顺序迭代。因此,如果 JSON.stringify 实现遵循这一点,则相等的对象(在 === 意义上但在 == 意义上不是必需的)将字符串化为相等的值。需要更多的研究。所以我猜你可以用相反的顺序对一个具有属性的对象进行邪恶的克隆,但我无法想象它会偶然发生......]至少在 Chrome 上,JSON.stringify 函数倾向于按照定义的顺序返回键(至少我已经注意到),但这种行为在任何时候都可能发生变化,不应依赖。如果您选择不在列表中使用对象,这应该可以正常工作。如果您的列表中确实有对象都具有唯一 id,则可以执行a1.map(function(x)}{return {id:x.uniqueId}})
. 如果您的列表中有任意对象,您可以继续阅读选项 #2。)
这也适用于嵌套数组。
然而,由于创建这些字符串和对其进行垃圾收集的开销,它的效率略低。
选项 2
历史版本 1 解决方案:
选项 3
选项 4:(2016 年编辑的延续)
这应该适用于大多数对象:
const STRICT_EQUALITY_BROKEN = (a,b)=> a===b;
const STRICT_EQUALITY_NO_NAN = (a,b)=> {
if (typeof a=='number' && typeof b=='number' && ''+a=='NaN' && ''+b=='NaN')
// isNaN does not do what you think; see +/-Infinity
return true;
else
return a===b;
};
function deepEquals(a,b, areEqual=STRICT_EQUALITY_NO_NAN, setElementsAreEqual=STRICT_EQUALITY_NO_NAN) {
/* compares objects hierarchically using the provided
notion of equality (defaulting to ===);
supports Arrays, Objects, Maps, ArrayBuffers */
if (a instanceof Array && b instanceof Array)
return arraysEqual(a,b, areEqual);
if (Object.getPrototypeOf(a)===Object.prototype && Object.getPrototypeOf(b)===Object.prototype)
return objectsEqual(a,b, areEqual);
if (a instanceof Map && b instanceof Map)
return mapsEqual(a,b, areEqual);
if (a instanceof Set && b instanceof Set) {
if (setElementsAreEqual===STRICT_EQUALITY_NO_NAN)
return setsEqual(a,b);
else
throw "Error: set equality by hashing not implemented because cannot guarantee custom notion of equality is transitive without programmer intervention."
}
if ((a instanceof ArrayBuffer || ArrayBuffer.isView(a)) && (b instanceof ArrayBuffer || ArrayBuffer.isView(b)))
return typedArraysEqual(a,b);
return areEqual(a,b); // see note[1] -- IMPORTANT
}
function arraysEqual(a,b, areEqual) {
if (a.length!=b.length)
return false;
for(var i=0; i<a.length; i++)
if (!deepEquals(a[i],b[i], areEqual))
return false;
return true;
}
function objectsEqual(a,b, areEqual) {
var aKeys = Object.getOwnPropertyNames(a);
var bKeys = Object.getOwnPropertyNames(b);
if (aKeys.length!=bKeys.length)
return false;
aKeys.sort();
bKeys.sort();
for(var i=0; i<aKeys.length; i++)
if (!areEqual(aKeys[i],bKeys[i])) // keys must be strings
return false;
return deepEquals(aKeys.map(k=>a[k]), aKeys.map(k=>b[k]), areEqual);
}
function mapsEqual(a,b, areEqual) { // assumes Map's keys use the '===' notion of equality, which is also the assumption of .has and .get methods in the spec; however, Map's values use our notion of the areEqual parameter
if (a.size!=b.size)
return false;
return [...a.keys()].every(k=>
b.has(k) && deepEquals(a.get(k), b.get(k), areEqual)
);
}
function setsEqual(a,b) {
// see discussion in below rest of StackOverflow answer
return a.size==b.size && [...a.keys()].every(k=>
b.has(k)
);
}
function typedArraysEqual(a,b) {
// we use the obvious notion of equality for binary data
a = new Uint8Array(a);
b = new Uint8Array(b);
if (a.length != b.length)
return false;
for(var i=0; i<a.length; i++)
if (a[i]!=b[i])
return false;
return true;
}
Demo (not extensively tested):
var nineTen = new Float32Array(2);
nineTen[0]=9; nineTen[1]=10;
> deepEquals(
[[1,[2,3]], 4, {a:5,'111':6}, new Map([['c',7],['d',8]]), nineTen],
[[1,[2,3]], 4, {111:6,a:5}, new Map([['d',8],['c',7]]), nineTen]
)
true
> deepEquals(
[[1,[2,3]], 4, {a:'5','111':6}, new Map([['c',7],['d',8]]), nineTen],
[[1,[2,3]], 4, {111:6,a:5}, new Map([['d',8],['c',7]]), nineTen],
(a,b)=>a==b
)
true
请注意,如果使用==
平等的概念,那么要知道虚假值和强制意味着==
平等不是可传递的。例如''==0
并且0=='0'
但是''!='0'
。这与 Sets 相关:我认为人们无法以有意义的方式覆盖 Set 相等的概念。如果使用 Set 相等的内置概念(即===
),则上述方法应该有效。但是,如果有人使用像 一样的非传递性概念==
,你就会打开一堆蠕虫:即使你强迫用户在域上定义一个散列函数(hash(a)!=hash(b) 意味着 a!=b ) 我不确定这会有所帮助...当然可以做 O(N^2) 性能的事情并删除成对的==
一个一个的项目,就像冒泡排序一样,然后再进行第二次 O(N^2) 传递以确认等价类中的事物实际上==
是彼此之间的,并且也是!=
如此配对的所有事物,但您仍然必须抛出一个运行时错误,如果你有一些强制进行......你也可能会得到奇怪的(但可能不是那么奇怪)与https://developer.mozilla.org/en-US/docs/Glossary/Falsy和Truthy 的边缘情况值(NaN==NaN 除外...但仅适用于集合!)。对于大多数同类数据类型集,这通常不是问题。
总结集合上递归等式的复杂性:
B.has(k) for every k in A
隐式使用===
-equality ( [1,2,3]!==[1,2,3]
)同义,而不是递归相等 ( deepEquals([1,2,3],[1,2,3]) == true
),所以两个new Set([[1,2,3]])
不相等,因为我们不递归Set
和Map
“等同” NaN 以将它们视为相同的值setKeys.sort((a,b)=> /*some comparison function*/)
因为 ecmascript (''==0 and 0=='0', but ''!='0'...自己定义一个,这肯定是一个崇高的目标)。.toString
或JSON.stringify
所有元素来帮助我们。然后我们将对它们进行排序,这为我们提供了潜在误报(两个不同的事物可能具有相同的字符串或 JSON 表示)的等价类(两个相同的事物不会具有相同的字符串 JSON 表示)。
Set
s的树;每个节点都属于 O(depth) 不同的序列化!因此,上面的实现声明如果项目只是简单的 ===(不是递归的 ===),则集合是相等的。这意味着它将为new Set([1,2,3])
and返回 false new Set([1,2,3])
。如果您知道自己在做什么,只需稍加努力,就可以重写该部分代码。
(旁注:地图是 es6 词典。我不知道它们是否具有 O(1) 或 O(log(N)) 查找性能,但无论如何它们都是“有序”的,因为它们会跟踪顺序其中插入了键值对。但是,如果元素以不同的顺序插入其中,则两个 Map 是否应该相等的语义是不明确的。我在下面给出了一个 deepEquals 的示例实现,它认为两个映射甚至相等如果元素以不同的顺序插入其中。)
(注意 [1]:重要提示:平等概念:您可能想用自定义的平等概念覆盖注释行,您还必须在其他函数出现的任何地方更改该概念。例如,你还是不'不是你想要 NaN==NaN 吗?默认情况下不是这样。还有更奇怪的东西,比如 0=='0'。你是否认为两个对象是相同的,当且仅当它们在内存?请参阅https://stackoverflow.com/a/5447170/711085。您应该记录您使用的平等概念。)另请注意,其他天真地使用.toString
并且.sort
有时可能会下降的答案祈祷这样一个事实,0!=-0
但被认为是平等的和对于几乎所有数据类型和 JSON 序列化,规范化为 0;是否-0==0 还应该记录在您的平等概念中,以及该表中的大多数其他内容(如 NaN 等)中。
您应该能够将上述内容扩展到 WeakMaps、WeakSets。不确定扩展到 DataViews 是否有意义。也应该能够扩展到 RegExps 等。
当你扩展它时,你意识到你做了很多不必要的比较。这是type
我之前定义的函数(解决方案#2)可以派上用场的地方;然后你可以立即发货。这是否值得(可能?不确定它在引擎盖下是如何工作的)表示类型的字符串的开销取决于您。然后您可以将调度程序(即 function )重写为deepEquals
如下所示:
var dispatchTypeEquals = {
number: function(a,b) {...a==b...},
array: function(a,b) {...deepEquals(x,y)...},
...
}
function deepEquals(a,b) {
var typeA = extractType(a);
var typeB = extractType(a);
return typeA==typeB && dispatchTypeEquals[typeA](a,b);
}
jQuery 没有比较数组的方法。然而,Underscore库(或类似的 Lodash 库)确实有这样一个方法:isEqual,它也可以处理各种其他情况(如对象文字)。坚持提供的例子:
var a=[1,2,3];
var b=[3,2,1];
var c=new Array(1,2,3);
alert(_.isEqual(a, b) + "|" + _.isEqual(b, c));
顺便说一句:Underscore 有很多其他 jQuery 也没有的方法,所以它是对 jQuery 的一个很好的补充。
编辑:正如评论中指出的那样,上面的内容现在仅在两个数组的元素顺序相同时才有效,即:
_.isEqual([1,2,3], [1,2,3]); // true
_.isEqual([1,2,3], [3,2,1]); // false
幸运的是 Javascript 有一个内置的方法来解决这个确切的问题,sort
:
_.isEqual([1,2,3].sort(), [3,2,1].sort()); // true
对于像数字和字符串这样的原始值,这是一个简单的解决方案:
a = [1,2,3]
b = [3,2,1]
a.sort().toString() == b.sort().toString()
调用sort()
will 确保元素的顺序无关紧要。该toString()
调用将创建一个字符串,其中的值以逗号分隔,因此可以测试两个字符串的相等性。
使用 JavaScript 1.6 版就这么简单:
Array.prototype.equals = function( array ) {
return this.length == array.length &&
this.every( function(this_i,i) { return this_i == array[i] } )
}
例如,[].equals([])
给出true
,而[1,2,3].equals( [1,3,2] )
产生false
。