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

Koe*_*err 225 javascript

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

alert(a == b + "|" + b == c);
Run Code Online (Sandbox Code Playgroud)

演示

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

jQuery是否提供任何方法?

eny*_*nyo 251

这是你应该做的.请不要使用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;
}
Run Code Online (Sandbox Code Playgroud)

  • (这不是我原来的代码,但编写它的人不喜欢发布它) (8认同)
  • @GijsjanB如果数组包含相同的对象,它确实有效.当然,如果对象不相同,并且您想要比较对象本身,则它不起作用.但那不是问题. (8认同)
  • 为什么要对代码进行排序? (3认同)
  • @VarunMadiath最后一个循环遍历每个元素并进行比较.如果数组未排序,那么如果项目的顺序不完全相同,它将失败. (3认同)
  • 如果数组包含对象,则此方法无效. (3认同)
  • 7 年后...可以使用一些性感的胖箭头来代替 for 循环... `return a.every((val, idx) =&gt; val === b[idx])` (3认同)
  • 你可以在答案中解释一下_why_我们不应该使用`stringify`或`<>`? (2认同)
  • @KunalKapadia的问题是针对JavaScript还是jQuery而不是框架。我肯定除了下划线以外,还有*许多*框架提供了此功能。 (2认同)

nin*_*cko 132

选项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);
}
Run Code Online (Sandbox Code Playgroud)

(如果您的数组包含对象,这可能不起作用.这是否仍然适用于对象取决于JSON实现是否对键进行排序.例如,JSON {1:2,3:4}可能或可能不等于{3:4,1:2};这取决于实现,并且规范使[2017更新:实际上,ES6规范现在保证对象键将按照以下顺序迭代:1)整数属性,2)属性按照它们被定义的顺序,然后3)符号属性按它们被定义的顺序.因此,如果JSON.stringify实现遵循此,则相等的对象(在===意义上但在==意义上不是NECESSARILY)将字符串化为相等的值.需要更多的研究.所以我猜你可以用相反的顺序制作一个具有属性的对象的邪恶克隆,但我无法想象它偶然发生...] 至少在Chrome上,JSON.stringify函数倾向于按顺序返回键定义了(至少我注意到了),但这种行为在任何时候都很容易改变,不应该依赖.如果您选择不在列表中使用对象,这应该可以正常工作.如果列表中的对象都具有唯一ID,则可以执行此操作a1.map(function(x)}{return {id:x.uniqueId}}).如果列表中有任意对象,则可以继续阅读选项#2.)

这也适用于嵌套数组.

然而,由于创建这些字符串和垃圾收集它们的开销,它的效率稍低.


选项2

更多"正确"选项,您可以覆盖它以处理特殊情况(如常规对象和null/undefined和自定义对象,如果您愿意):

// 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]})
    });
}
Run Code Online (Sandbox Code Playgroud)

 

// 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});
Run Code Online (Sandbox Code Playgroud)

 

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);
}
Run Code Online (Sandbox Code Playgroud)

演示:

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
Run Code Online (Sandbox Code Playgroud)

要像常规函数一样使用它,请执行以下操作:

function allDeepEqual2() {
    return allDeepEqual([].slice.call(arguments));
}
Run Code Online (Sandbox Code Playgroud)

演示:

allDeepEqual2([[1,2],3], [[1,2],3])
true
Run Code Online (Sandbox Code Playgroud)

选项3

编辑:这是2016年,我以前过于复杂的答案让我烦恼.这种递归的,命令式的"递归编程101"实现使代码非常简单,并且在尽可能早的时候失败(给我们效率).它也不会产生多余的短暂数据结构(并不是说函数编程通常有任何问题,只是在这里保持干净).

如果我们想将它应用于非空数组数组,我们可以执行seriesOfArrays.reduce(arraysEqual).

这是它自己的函数,而不是使用Object.defineProperties连接到Array.prototype,因为如果我们传入一个未定义的值(如果你想这样做,那么这是一个很好的设计决定)会因为键错误而失败.

这只回答OP原始问题.

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
    }
}
Run Code Online (Sandbox Code Playgroud)

例子:

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
Run Code Online (Sandbox Code Playgroud)

如果你想用js对象将它应用于类似JSON的数据结构,你可以这样做.幸运的是,我们保证所有对象键都是唯一的,因此迭代对象OwnProperties并按键对它们进行排序,然后断言排序的键数组相等且值数组相等,并且只是递归.我们可以扩展它以包括地图(其中键也是唯一的).(但是如果我们将它扩展到Sets,我们会遇到树同构问题http://logic.pdmi.ras.ru/~smal/files/smal_jass08_slides.pdf - 幸运的是它并不像一般的图同构那么难;有事实上是一个O(#vertices)算法来解决它,但它可以变得非常复杂有效地完成它.病理情况是如果你有一个由许多看似无法区分的对象组成的集合,但是在进一步检查时会有一些这些对象你可以深入研究它们.你也可以通过使用散列来解决几乎所有情况.)


选项4 :(继续编辑2016年)

这适用于大多数对象:

function deepEquals(a,b) {
    if (a instanceof Array && b instanceof Array)
        return arraysEqual(a,b);
    if (Object.getPrototypeOf(a)===Object.prototype && Object.getPrototypeOf(b)===Object.prototype)
        return objectsEqual(a,b);
    if (a instanceof Map && b instanceof Map)
        return mapsEqual(a,b);        
    if (a instanceof Set && b instanceof Set)
        throw "Error: set equality by hashing not implemented."
    if ((a instanceof ArrayBuffer || ArrayBuffer.isView(a)) && (b instanceof ArrayBuffer || ArrayBuffer.isView(b)))
        return typedArraysEqual(a,b);
    return a==b;  // see note[1] -- IMPORTANT
}

function arraysEqual(a,b) {
    if (a.length!=b.length)
        return false;
    for(var i=0; i<a.length; i++)
        if (!deepEquals(a[i],b[i]))
            return false;
    return true;
}
function objectsEqual(a,b) {
    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 (aKeys[i]!=bKeys[i]) // keys must be strings
            return false;
    return deepEquals(aKeys.map(k=>a[k]), aKeys.map(k=>b[k]));
}
function mapsEqual(a,b) {
    if (a.size!=b.size)
        return false;
    var aPairs = Array.from(a);
    var bPairs = Array.from(b);
    aPairs.sort((x,y) => x[0]<y[0]);
    bPairs.sort((x,y) => x[0]<y[0]);
    for(var i=0; i<a.length; i++)
        if (!deepEquals(aPairs[i][0],bPairs[i][0]) || !deepEquals(aPairs[i][1],bPairs[i][1]))
            return false;
    return true;
}
function typedArraysEqual(a,b) {
    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;
}
Run Code Online (Sandbox Code Playgroud)

演示(未经过广泛测试):

var nineTen = new Float32Array(2);
nineTen[0]=9; nineTen[1]=10;
deepEquals(
    [[1,[2,3]], 4, {a:5,b:6}, new Map([['c',7],['d',8]]), nineTen],
    [[1,[2,3]], 4, {b:6,a:5}, new Map([['d',8],['c',7]]), nineTen]
)
Run Code Online (Sandbox Code Playgroud)

(旁注:地图是es6词典.我不知道它们是否具有O(1)或O(log(N))查找性能,但无论如何它们都是"有序"的,因为它们跟踪顺序但是,如果元素以不同的顺序插入到它们中,两个Maps是否应该相等的语义是不明确的.我给出了一个深度平等的示例实现,它认为两个映射相等如果元素以不同的顺序插入它们.)

(注释[1]:重要:平等的注释:您可能希望使用自定义的相等概念覆盖注释的行,您必须在其出现的任何位置更改其他功能.例如,您是否要做你想要NaN == NaN吗?默认情况并非如此.还有更奇怪的事情,比如0 =='0'.你是否认为两个对象是相同的,当且仅当它们是同一个对象时内存?请参阅/sf/answers/381301931/.您应该记录您使用的相等概念.)

您应该能够将上述内容扩展到WeakMaps,WeakSets.不确定扩展到DataViews是否有意义.也应该可以扩展到RegExps等等.

当你扩展它时,你会发现你做了很多不必要的比较.这是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);
}
Run Code Online (Sandbox Code Playgroud)

  • +1,一点评论:对于`allTrue`,您还可以使用`array.every` 和一个返回数组元素值的函数。 (2认同)
  • `JSON.stringify(null)==='null'`(字符串"null"),而不是"null". (2认同)

mac*_*ost 66

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));
Run Code Online (Sandbox Code Playgroud)

顺便说一句:Underscore还有很多其他jQuery缺失的方法,所以它是jQuery的一个很好的补充.

编辑:正如在评论中指出的,上面现在只有两个数组的元素顺序相同时才有效,即:

_.isEqual([1,2,3], [1,2,3]); // true
_.isEqual([1,2,3], [3,2,1]); // false
Run Code Online (Sandbox Code Playgroud)

幸运的是,Javascript有一个内置的方法来解决这个确切的问题,sort:

_.isEqual([1,2,3].sort(), [3,2,1].sort()); // true
Run Code Online (Sandbox Code Playgroud)

  • Underscorejs是我每天使用的lib.它的简单性肯定会高得多. (3认同)
  • 我喜欢lodash,它确实包含一套超级Underscore的功能......但它不是*"优越",也没有"替换"Underscore.首先,Lodash已经进行了元编程和微优化,其源代码基本上是不可读的.当(例如)你不小心将错误的参数传递给Lodash/Underscore函数并且必须调试正在进行的操作时,这很重要.在这种情况下,Underscore*非常优越*因为(与Lodash不同)你实际上可以阅读源代码.最终,两个图书馆都不优于另一个图书馆,它们只是有不同的优点和缺点. (2认同)
  • 回到手头的话题,这会提示 `false|false`。`_.isEqual(a,b)` 根据它们的顺序比较数组的元素,因此如果需要顺序不敏感的比较,则必须在比较之前对数组进行排序。 (2认同)

小智 37

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

a = [1,2,3]

b = [3,2,1]

a.sort().toString() == b.sort().toString() 
Run Code Online (Sandbox Code Playgroud)

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

  • 如果数组包含除简单值之外的任何内容,请注意.`Array.prototype.sort()`很浅,`Array.prototype.toString()`将对象转换为`[object Object]`并展平任何嵌入的数组,这可能会导致误报. (5认同)
  • 我不明白为什么这个被低估了:这是到目前为止唯一的解决方案,实际上得到(指定不足)的例子正确... (5认同)
  • 啊,你是对的 - 但是问题仍然存在:`['1,2',3].toString() === [1,'2,3'].toString()` 是误报 (4认同)
  • `a.sort()`不仅返回排序版本.它改变了数组本身,可能会以意想不到的方式影响一个人的应用程序. (3认同)
  • 这是错误的,因为比较`[12,34,56]`到`[1,23,456]` (2认同)
  • @feihcsim你错了。[12,34,56] .toString()//结果:“ 12,34,56”而[1,23,456] .toString()//结果:“ 1,23,456”因此,它们不相等 (2认同)

rpl*_*iko 22

使用JavaScript 1.6版,它就像这样简单:

Array.prototype.equals = function( array ) {
  return this.length == array.length && 
         this.every( function(this_i,i) { return this_i == array[i] } )  
  }
Run Code Online (Sandbox Code Playgroud)

例如,[].equals([])给出true,而[1,2,3].equals( [1,3,2] )产量false.

  • 通常建议不要修改/扩展现有的全局对象. (8认同)
  • 更好用===而不是==对吗? (2认同)

Cla*_*wsy 8

即使这看起来非常简单,但有时它确实非常有用.如果您只需要查看两个数组是否具有相同的项并且它们的顺序相同,请尝试以下操作:

[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
Run Code Online (Sandbox Code Playgroud)

但是,这不适用于模式高级情况,例如:

[[1,2],[3]].toString() == [[1],[2,3]].toString()
true
Run Code Online (Sandbox Code Playgroud)

这取决于你需要什么.


小智 7

根据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
Run Code Online (Sandbox Code Playgroud)

  • 正如[这个答案](http://stackoverflow.com/a/9334251/950430)所指出的,`arrays_equal([1,[2,3]],[[1,2],3])`会返回TRUE;. (5认同)
  • 由于Javascript中的`null == null`(甚至更多,`null === null`),在数组等价检查中将两个空值视为相等也不合适吗? (3认同)
  • 同样,将'arrays_equal([“ 1,2”],[“ 1,2”])`视为相等,因此将'arrays_equal([],[“”]))也视为相等。 (2认同)

uKo*_*lka 5

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;
};
Run Code Online (Sandbox Code Playgroud)

测试:

// 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.');
Run Code Online (Sandbox Code Playgroud)


小智 5

检查数组的大小后,通过 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;
    }
}
Run Code Online (Sandbox Code Playgroud)