如何检查两个数组是否与 JavaScript 相等?

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


alert(a == b + "|" + b == c);

小样

如何检查这些数组是否相等,并得到一个返回 true的方法,如果它们是相等的?

JQuery 为此提供了什么方法吗?

648478 次浏览

根据Tim James 回答和Fox32的注释,下面应该检查空值,假设两个空值不相等。

function arrays_equal(a,b) { return !!a && !!b && !(a<b || b<a); }


> arrays_equal([1,2,3], [1,3,4])
false
> arrays_equal([1,2,3], [1,2,3])
true
> arrays_equal([1,3,4], [1,2,3])
false
> arrays_equal(null, [1,2,3])
false
> arrays_equal(null, null)
false

[2021更新日志:修复了js对象上的option4: 没有总排序的错误(甚至不包括NaN!=NaN'5'==5 ('5'===5'2'<3等)),因此不能在Map.keys()上使用.sort(cmpFunc)(尽管可以在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实现是否对键进行排序。例如,{1:2,3:4}的JSON可能等于也可能不等于{3:4,1:2};这取决于实现,规范没有任何保证。[2017年更新:实际上ES6规范现在保证对象键将按照1)整数属性,2)属性按照它们定义的顺序迭代,然后3)符号属性按照它们定义的顺序迭代。因此,如果JSON。stringify实现遵循这一点,相等的对象(在===意义上,但不一定在==意义上)将被stringify为相等的值。需要更多的研究。所以我猜你可以用相反的顺序制作一个邪恶的对象克隆,但我无法想象它会意外发生……如果你选择不使用列表中的对象,这应该可以正常工作。如果列表中确实有对象都有唯一id,则可以执行a1.map(function(x)}{return {id:x.uniqueId}})。如果你的列表中有任意的对象,你可以继续阅读选项#2。)

这也适用于嵌套数组。

但是,由于创建这些字符串和对它们进行垃圾回收的开销,它的效率略低。


选项2

历史版本1解决方案:

// generally useful functions
function type(x) { // does not work in general, but works on JSONable objects we care about... modify as you see fit
// e.g.  type(/asdf/g) --> "[object RegExp]"
return Object.prototype.toString.call(x);
}
function zip(arrays) {
// e.g. zip([[1,2,3],[4,5,6]]) --> [[1,4],[2,5],[3,6]]
return arrays[0].map(function(_,i){
return arrays.map(function(array){return array[i]})
});
}




// helper functions
function allCompareEqual(array) {
// e.g.  allCompareEqual([2,2,2,2]) --> true
// does not work with nested arrays or objects
return array.every(function(x){return x==array[0]});
}


function isArray(x){ return type(x)==type([]) }
function getLength(x){ return x.length }
function allTrue(array){ return array.reduce(function(a,b){return a&&b},true) }
// e.g. allTrue([true,true,true,true]) --> true
// or just array.every(function(x){return x});




function allDeepEqual(things) {
// works with nested arrays
if( things.every(isArray) )
return allCompareEqual(things.map(getLength))     // all arrays of same length
&& allTrue(zip(things).map(allDeepEqual)); // elements recursively equal


//else if( this.every(isObject) )
//  return {all have exactly same keys, and for
//          each key k, allDeepEqual([o1[k],o2[k],...])}
//  e.g. ... && allTrue(objectZip(objects).map(allDeepEqual))


//else if( ... )
//  extend some more


else
return allCompareEqual(things);
}


// Demo:


allDeepEqual([ [], [], [] ])
true
allDeepEqual([ [1], [1], [1] ])
true
allDeepEqual([ [1,2], [1,2] ])
true
allDeepEqual([ [[1,2],[3]], [[1,2],[3]] ])
true


allDeepEqual([ [1,2,3], [1,2,3,4] ])
false
allDeepEqual([ [[1,2],[3]], [[1,2],[],3] ])
false
allDeepEqual([ [[1,2],[3]], [[1],[2,3]] ])
false
allDeepEqual([ [[1,2],3], [1,[2,3]] ])
false
<!--


More "proper" option, which you can override to deal with special cases (like regular objects and null/undefined and custom objects, if you so desire):


To use this like a regular function, do:


function allDeepEqual2() {
return allDeepEqual([].slice.call(arguments));
}


Demo:


allDeepEqual2([[1,2],3], [[1,2],3])
true
    

-->


选项3

function arraysEqual(a,b) {
/*
Array-aware equality checker:
Returns whether arguments a and b are == to each other;
however if they are equal-lengthed arrays, returns whether their
elements are pairwise == to each other recursively under this
definition.
*/
if (a instanceof Array && b instanceof Array) {
if (a.length!=b.length)  // assert same length
return false;
for(var i=0; i<a.length; i++)  // assert each element equal
if (!arraysEqual(a[i],b[i]))
return false;
return true;
} else {
return a==b;  // if not both arrays, should be the same
}
}


//Examples:


arraysEqual([[1,2],3], [[1,2],3])
true
arraysEqual([1,2,3], [1,2,3,4])
false
arraysEqual([[1,2],[3]], [[1,2],[],3])
false
arraysEqual([[1,2],[3]], [[1],[2,3]])
false
arraysEqual([[1,2],3], undefined)
false
arraysEqual(undefined, undefined)
true
arraysEqual(1, 2)
false
arraysEqual(null, null)
true
arraysEqual(1, 1)
true
arraysEqual([], 1)
false
arraysEqual([], undefined)
false
arraysEqual([], [])
true
/*
If you wanted to apply this to JSON-like data structures with js Objects, you could do so. Fortunately we're guaranteed that all objects keys are unique, so iterate over the objects OwnProperties and sort them by key, then assert that both the sorted key-array is equal and the value-array are equal, and just recurse. We CANNOT extend the sort-then-compare method with Maps as well; even though Map keys are unique, there is no total ordering in ecmascript, so you can't sort them... but you CAN query them individually (see the next section Option 4). (Also if we extend this to Sets, we run into the tree isomorphism problem http://logic.pdmi.ras.ru/~smal/files/smal_jass08_slides.pdf - fortunately it's not as hard as general graph isomorphism; there is in fact an O(#vertices) algorithm to solve it, but it can get very complicated to do it efficiently. The pathological case is if you have a set made up of lots of seemingly-indistinguishable objects, but upon further inspection some of those objects may differ as you delve deeper into them. You can also work around this by using hashing to reject almost all cases.)
*/
<!--
**edit**: It's 2016 and my previous overcomplicated answer was bugging me. This recursive, imperative "recursive programming 101" implementation keeps the code really simple, and furthermore fails at the earliest possible point (giving us efficiency). It also doesn't generate superfluous ephemeral datastructures (not that there's anything wrong with functional programming in general, but just keeping it clean here).


If we wanted to apply this to a non-empty arrays of arrays, we could do seriesOfArrays.reduce(arraysEqual).


This is its own function, as opposed to using Object.defineProperties to attach to Array.prototype, since that would fail with a key error if we passed in an undefined value (that is however a fine design decision if you want to do so).


This only answers OPs original question.
-->


< p >选项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

注意,如果使用==相等的概念,那么要知道错误的值和强制意味着==相等是不可传递的。例如''==00=='0'但是''!='0'。这与集合相关:我不认为人们可以以一种有意义的方式覆盖集合相等的概念。如果使用的是内置的集合相等的概念(即===),则上述方法应该有效。然而,如果一个人使用像==这样的非传递的相等概念,你就打开了一个蠕虫:即使你强迫用户在域中定义一个哈希函数(hash(a)!=hash(b)暗示a!=b),我不确定这是否有帮助……当然,人们可以做O(N^2)性能的事情,并像冒泡式排序一样逐个删除==项对,然后再做第二次O(N^2)传递,以确认等价类中的内容实际上彼此是==,并且对没有这样配对的所有内容也是!=,但如果你进行了一些强制操作,你仍然必须抛出一个运行时错误……==0和Truthy值的边缘情况也可能很奇怪(但可能没有那么奇怪)(除了NaN==NaN…但只针对布景!)。对于大多数同质数据类型的集合,这通常不是问题。

总结集合上递归等式的复杂性:

  • 集合相等是树同构问题http://logic.pdmi.ras.ru/~smal/files/smal_jass08_slides.pdf,但更简单一点
  • set A =?set B与B.has(k) for every k in A同义,隐式使用__abc1 -equal ([1,2,3]!==[1,2,3]),而不是递归equal (deepEquals([1,2,3],[1,2,3]) == true),因此两个new Set([[1,2,3]])不相等,因为我们不递归
  • 如果你使用的递归等价概念不是1)自反的(a=b暗示b=a)和2)对称的(a=a)和3)传递的(a=b和b=c暗示a=c),那么试图让递归等价起作用是没有意义的;这是等价类的定义
  • 等式==运算符显然不符合这些属性中的许多
  • 甚至ecmascript中的严格相等= = =操作符 不遵守这些属性,因为ecmascript严格的相等比较算法有NaN!=NaN;这就是为什么许多原生数据类型,如SetMap“等同”nan,当它们作为键
  • 时,将它们视为相同的值 只要我们强制并确保递归集相等确实是传递的、自反的和对称的,我们就可以确保没有可怕的错误发生。
    • 然后,我们可以做O(N²)比较通过递归随机比较所有东西,这是非常低效的。没有什么神奇的算法可以让我们执行setKeys.sort((a,b)=> /*some comparison function*/),因为在ecmascript中没有总排序(" ==0和0=='0',但是" !='0'…虽然我相信你可以自己定义一个,这肯定是一个崇高的目标)。
    • 然而,我们可以.toStringify或JSON.stringify所有元素来帮助我们。然后我们将对它们进行排序,这为我们提供了潜在假阳性(两个不同的东西可能具有相同的字符串或JSON表示)的等价类(两个相同的东西不会没有相同的字符串或JSON表示)。
      • 然而,这也带来了它自己的性能问题,因为序列化同一个东西,然后一遍又一遍地序列化那个东西的子集,是非常低效的。想象一个嵌套的__abc0树;每个节点将属于O(深度)不同的序列化!
      • 即使这不是一个问题,如果所有序列化“提示”都相同,那么最坏的性能仍然是O(N!)

因此,上面的实现声明如果项只是简单的===(不是递归地===),则set是相等的。这意味着new Set([1,2,3])new Set([1,2,3])将返回false。如果您知道自己在做什么,那么只需花点功夫,就可以重写这部分代码。

(旁注:映射是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,等等。

您应该能够将上述扩展到弱映射、弱集。不确定扩展到DataViews是否有意义。应该也能够扩展到regexp等等。

当您扩展它时,您会意识到您做了许多不必要的比较。这就是我之前定义的type函数(解决方案#2)可以派上用场的地方;然后你就可以立即调度。这是否值得(可能吗?)不确定它在引擎盖下是如何工作的)字符串表示类型是由你。然后你可以重写分派器,即函数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);
}

即使这看起来超级简单,但有时它真的很有用。如果你所需要的只是看看两个数组是否有相同的项,并且它们的顺序相同,试试这个:

[1, 2, 3].toString() == [1, 2, 3].toString()
true
[1, 2, 3,].toString() == [1, 2, 3].toString()
true
[1,2,3].toString() == [1, 2, 3].toString()
true

然而,这并不适用于模式高级情况,如:

[[1,2],[3]].toString() == [[1],[2,3]].toString()
true

这取决于你需要什么。

这种方法很糟糕,但我把它留在这里作为参考,所以其他人会避免这种方法:


使用@ninjagecko的选项1最适合我:

Array.prototype.equals = function(array) {
return array instanceof Array && JSON.stringify(this) === JSON.stringify(array) ;
}


a = [1, [2, 3]]
a.equals([[1, 2], 3]) // false
a.equals([1, [2, 3]]) // true

它还将处理null和未定义的情况,因为我们将它添加到数组的原型中,并检查另一个参数是否也是一个数组。

jQuery有这样的方法对于深层递归比较

一个自定义的通用严格的平等检查可以如下所示:

function deepEquals(obj1, obj2, parents1, parents2) {
"use strict";
var i;
// compare null and undefined
if (obj1 === undefined || obj2 === undefined ||
obj1 === null || obj2 === null) {
return obj1 === obj2;
}


// compare primitives
if (typeof (obj1) !== 'object' || typeof (obj2) !== 'object') {
return obj1.valueOf() === obj2.valueOf();
}


// if objects are of different types or lengths they can't be equal
if (obj1.constructor !== obj2.constructor || (obj1.length !== undefined && obj1.length !== obj2.length)) {
return false;
}


// iterate the objects
for (i in obj1) {
// build the parents list for object on the left (obj1)
if (parents1 === undefined) parents1 = [];
if (obj1.constructor === Object) parents1.push(obj1);
// build the parents list for object on the right (obj2)
if (parents2 === undefined) parents2 = [];
if (obj2.constructor === Object) parents2.push(obj2);
// walk through object properties
if (obj1.propertyIsEnumerable(i)) {
if (obj2.propertyIsEnumerable(i)) {
// if object at i was met while going down here
// it's a self reference
if ((obj1[i].constructor === Object && parents1.indexOf(obj1[i]) >= 0) || (obj2[i].constructor === Object && parents2.indexOf(obj2[i]) >= 0)) {
if (obj1[i] !== obj2[i]) {
return false;
}
continue;
}
// it's not a self reference so we are here
if (!deepEquals(obj1[i], obj2[i], parents1, parents2)) {
return false;
}
} else {
// obj2[i] does not exist
return false;
}
}
}
return true;
};

测试:

// message is displayed on failure
// clean console === all tests passed
function assertTrue(cond, msg) {
if (!cond) {
console.log(msg);
}
}


var a = 'sdf',
b = 'sdf';
assertTrue(deepEquals(b, a), 'Strings are equal.');
b = 'dfs';
assertTrue(!deepEquals(b, a), 'Strings are not equal.');
a = 9;
b = 9;
assertTrue(deepEquals(b, a), 'Numbers are equal.');
b = 3;
assertTrue(!deepEquals(b, a), 'Numbers are not equal.');
a = false;
b = false;
assertTrue(deepEquals(b, a), 'Booleans are equal.');
b = true;
assertTrue(!deepEquals(b, a), 'Booleans are not equal.');
a = null;
assertTrue(!deepEquals(b, a), 'Boolean is not equal to null.');
a = function () {
return true;
};
assertTrue(deepEquals(
[
[1, 1, 1],
[2, 'asdf', [1, a]],
[3, {
'a': 1.0
},
true]
],
[
[1, 1, 1],
[2, 'asdf', [1, a]],
[3, {
'a': 1.0
},
true]
]), 'Arrays are equal.');
assertTrue(!deepEquals(
[
[1, 1, 1],
[2, 'asdf', [1, a]],
[3, {
'a': 1.0
},
true]
],
[
[1, 1, 1],
[2, 'asdf', [1, a]],
[3, {
'a': '1'
},
true]
]), 'Arrays are not equal.');
a = {
prop: 'val'
};
a.self = a;
b = {
prop: 'val'
};
b.self = a;
assertTrue(deepEquals(b, a), 'Immediate self referencing objects are equal.');
a.prop = 'shmal';
assertTrue(!deepEquals(b, a), 'Immediate self referencing objects are not equal.');
a = {
prop: 'val',
inside: {}
};
a.inside.self = a;
b = {
prop: 'val',
inside: {}
};
b.inside.self = a;
assertTrue(deepEquals(b, a), 'Deep self referencing objects are equal.');
b.inside.self = b;
assertTrue(!deepEquals(b, a), 'Deep self referencing objects are not equeal. Not the same instance.');
b.inside.self = {foo: 'bar'};
assertTrue(!deepEquals(b, a), 'Deep self referencing objects are not equal. Completely different object.');
a = {};
b = {};
a.self = a;
b.self = {};
assertTrue(!deepEquals(b, a), 'Empty object and self reference of an empty object.');

对于数字和字符串等原始值,这是一个简单的解决方案:

a = [1,2,3]


b = [3,2,1]


a.sort().toString() == b.sort().toString()

调用sort()将确保元素的顺序无关紧要。toString()调用将创建一个值以逗号分隔的字符串,以便可以测试两个字符串是否相等。

jQuery没有比较数组的方法。然而,强调图书馆(或类似的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));

顺便说一句:下划线还有很多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

这是你应该做的。请不要使用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;
}

在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

要做到这一点并不容易。我也需要这个,但我想要一个函数,可以接受任何两个变量,并测试是否相等。这包括非对象值、对象、数组和任何级别的嵌套。

在您的问题中,您提到希望忽略数组中值的顺序。我的解决方案本身并没有这样做,但您可以通过在比较是否相等之前对数组进行排序来实现

我还想将非对象转换为字符串,这样[1,2]===["1",2]

由于我的项目使用了UnderscoreJs,所以我决定将其作为一个mixin,而不是一个独立的函数。

你可以在http://jsfiddle.net/nemesarial/T44W4/上测试它

这是我的mxin:

_.mixin({
/**
Tests for the equality of two variables
valA: first variable
valB: second variable
stringifyStatics: cast non-objects to string so that "1"===1
**/
equal:function(valA,valB,stringifyStatics){
stringifyStatics=!!stringifyStatics;


//check for same type
if(typeof(valA)!==typeof(valB)){
if((_.isObject(valA) || _.isObject(valB))){
return false;
}
}


//test non-objects for equality
if(!_.isObject(valA)){
if(stringifyStatics){
var valAs=''+valA;
var valBs=''+valB;
ret=(''+valA)===(''+valB);
}else{
ret=valA===valB;
}
return ret;
}


//test for length
if(_.size(valA)!=_.size(valB)){
return false;
}


//test for arrays first
var isArr=_.isArray(valA);


//test whether both are array or both object
if(isArr!==_.isArray(valB)){
return false;
}


var ret=true;
if(isArr){
//do test for arrays
_.each(valA,function(val,idx,lst){
if(!ret){return;}
ret=ret && _.equal(val,valB[idx],stringifyStatics);
});
}else{
//do test for objects
_.each(valA,function(val,idx,lst){
if(!ret){return;}


//test for object member exists
if(!_.has(valB,idx)){
ret=false;
return;
}


// test for member equality
ret=ret && _.equal(val,valB[idx],stringifyStatics);
});


}
return ret;
}
});

下面是它的用法:

_.equal([1,2,3],[1,2,"3"],true)

要演示嵌套,你可以这样做:

_.equal(
['a',{b:'b',c:[{'someId':1},2]},[1,2,3]],
['a',{b:'b',c:[{'someId':"1"},2]},["1",'2',3]]
,true);

它处理所有可能的东西,甚至在对象的结构中引用自己。您可以在代码的末尾看到示例。

var deepCompare = (function() {
function internalDeepCompare (obj1, obj2, objects) {
var i, objPair;


if (obj1 === obj2) {
return true;
}


i = objects.length;
while (i--) {
objPair = objects[i];
if (  (objPair.obj1 === obj1 && objPair.obj2 === obj2) ||
(objPair.obj1 === obj2 && objPair.obj2 === obj1)  ) {
return true;
}
}
objects.push({obj1: obj1, obj2: obj2});


if (obj1 instanceof Array) {
if (!(obj2 instanceof Array)) {
return false;
}


i = obj1.length;


if (i !== obj2.length) {
return false;
}


while (i--) {
if (!internalDeepCompare(obj1[i], obj2[i], objects)) {
return false;
}
}
}
else {
switch (typeof obj1) {
case "object":
// deal with null
if (!(obj2 && obj1.constructor === obj2.constructor)) {
return false;
}


if (obj1 instanceof RegExp) {
if (!(obj2 instanceof RegExp && obj1.source === obj2.source)) {
return false;
}
}
else if (obj1 instanceof Date) {
if (!(obj2 instanceof Date && obj1.getTime() === obj2.getTime())) {
return false;
}
}
else {
for (i in obj1) {
if (obj1.hasOwnProperty(i)) {
if (!(obj2.hasOwnProperty(i) && internalDeepCompare(obj1[i], obj2[i], objects))) {
return false;
}
}
}
}
break;
case "function":
if (!(typeof obj2 === "function" && obj1+"" === obj2+"")) {
return false;
}
break;
default:                 //deal with NaN
if (obj1 !== obj2 && obj1 === obj1 && obj2 === obj2) {
return false;
}
}
}


return true;
}


return function (obj1, obj2) {
return internalDeepCompare(obj1, obj2, []);
};
}());


/*
var a = [a, undefined, new Date(10), /.+/, {a:2}, function(){}, Infinity, -Infinity, NaN, 0, -0, 1, [4,5], "1", "-1", "a", null],
b = [b, undefined, new Date(10), /.+/, {a:2}, function(){}, Infinity, -Infinity, NaN, 0, -0, 1, [4,5], "1", "-1", "a", null];
deepCompare(a, b);
*/
var a= [1, 2, 3, '3'];
var b = [1, 2, 3];


var c = a.filter(function (i) { return ! ~b.indexOf(i); });


alert(c.length);

使用map()reduce():

function arraysEqual (a1, a2) {
return a1 === a2 || (
a1 !== null && a2 !== null &&
a1.length === a2.length &&
a1
.map(function (val, idx) { return val === a2[idx]; })
.reduce(function (prev, cur) { return prev && cur; }, true)
);
}

如果你想检查相等的对象数组和顺序并不重要,即。

areEqual([{id: "0"}, {id: "1"}], [{id: "1"}, {id: "0"}]) // true

首先要对数组进行排序。lodash有你需要的所有工具,通过组合sortByisEqual:

// arr1 & arr2: Arrays of objects
// sortProperty: the property of the object with which you want to sort
// Note: ensure every object in both arrays has your chosen sortProperty
// For example, arr1 = [{id: "v-test_id0"}, {id: "v-test_id1"}]
// and          arr2 = [{id: "v-test_id1"}, {id: "v-test_id0"}]
// sortProperty should be 'id'


function areEqual (arr1, arr2, sortProperty) {
return _.areEqual(_.sortBy(arr1, sortProperty), _.sortBy(arr2, sortProperty))
}

编辑:由于sortBy返回一个新数组,在排序之前不需要克隆数组。原始数组不会被改变。

注意,对于lodash的isEqual顺序很重要。如果sortBy没有首先应用到每个数组,上面的例子将返回false

如果你正在使用lodash并且不想修改任何一个数组,你可以使用函数_.xor()。它将两个数组作为集合进行比较,并返回包含它们之差的集合。如果此差值的长度为零,则两个数组本质上相等:

var a = [1, 2, 3];
var b = [3, 2, 1];
var c = new Array(1, 2, 3);
_.xor(a, b).length === 0
true
_.xor(b, c).length === 0
true

在检查数组大小之后,使用for循环检查每个值。

function equalArray(a, b) {
if (a.length === b.length) {
for (var i = 0; i < a.length; i++) {
if (a[i] !== b[i]) {
return false;
}
}
return true;
} else {
return false;
}
}