如何比较 JavaScript 中的数组?

IT技术 javascript arrays json
2020-12-29 23:15:00

我想比较两个数组......理想情况下,有效地。没什么特别的,只要true它们是相同的,false如果不是。毫不奇怪,比较运算符似乎不起作用。

var a1 = [1,2,3];
var a2 = [1,2,3];
console.log(a1==a2);    // Returns false
console.log(JSON.stringify(a1)==JSON.stringify(a2));    // Returns true

JSON 编码每个数组都可以,但是有没有更快或“更好”的方法来简单地比较数组而不必遍历每个值?

6个回答

要比较数组,请遍历它们并比较每个值:

比较数组:

// Warn if overriding existing method
if(Array.prototype.equals)
    console.warn("Overriding existing Array.prototype.equals. Possible causes: New API defines the method, there's a framework conflict or you've got double inclusions in your code.");
// attach the .equals method to Array's prototype to call it on any array
Array.prototype.equals = function (array) {
    // if the other array is a falsy value, return
    if (!array)
        return false;

    // compare lengths - can save a lot of time 
    if (this.length != array.length)
        return false;

    for (var i = 0, l=this.length; i < l; i++) {
        // Check if we have nested arrays
        if (this[i] instanceof Array && array[i] instanceof Array) {
            // recurse into the nested arrays
            if (!this[i].equals(array[i]))
                return false;       
        }           
        else if (this[i] != array[i]) { 
            // Warning - two different object instances will never be equal: {x:20} != {x:20}
            return false;   
        }           
    }       
    return true;
}
// Hide method from for-in loops
Object.defineProperty(Array.prototype, "equals", {enumerable: false});

用法:

[1, 2, [3, 4]].equals([1, 2, [3, 2]]) === false;
[1, "2,3"].equals([1, 2, 3]) === false;
[1, 2, [3, 4]].equals([1, 2, [3, 4]]) === true;
[1, 2, 1, 2].equals([1, 2, 1, 2]) === true;

您可能会说“但是比较字符串要快得多 - 没有循环...... ”好吧,那么您应该注意存在 ARE 循环。第一个递归循环将 Array 转换为字符串,第二个递归循环比较两个字符串。所以这种方法比使用 string 更快

我相信更大量的数据应该始终存储在数组中,而不是存储在对象中。但是,如果您使用对象,也可以部分比较它们。
就是这样:

比较对象:

我在上面说过,两个对象实例永远不会相等,即使它们此刻包含相同的数据:

({a:1, foo:"bar", numberOfTheBeast: 666}) == ({a:1, foo:"bar", numberOfTheBeast: 666})  //false

这是有原因的,因为例如对象中可能存在私有变量。

但是,如果您只是使用对象结构来包含数据,则仍然可以进行比较:

Object.prototype.equals = function(object2) {
    //For the first loop, we only check for types
    for (propName in this) {
        //Check for inherited methods and properties - like .equals itself
        //https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwnProperty
        //Return false if the return value is different
        if (this.hasOwnProperty(propName) != object2.hasOwnProperty(propName)) {
            return false;
        }
        //Check instance type
        else if (typeof this[propName] != typeof object2[propName]) {
            //Different types => not equal
            return false;
        }
    }
    //Now a deeper check using other objects property names
    for(propName in object2) {
        //We must check instances anyway, there may be a property that only exists in object2
            //I wonder, if remembering the checked values from the first loop would be faster or not 
        if (this.hasOwnProperty(propName) != object2.hasOwnProperty(propName)) {
            return false;
        }
        else if (typeof this[propName] != typeof object2[propName]) {
            return false;
        }
        //If the property is inherited, do not check any more (it must be equa if both objects inherit it)
        if(!this.hasOwnProperty(propName))
          continue;
        
        //Now the detail check and recursion
        
        //This returns the script back to the array comparing
        /**REQUIRES Array.equals**/
        if (this[propName] instanceof Array && object2[propName] instanceof Array) {
                   // recurse into the nested arrays
           if (!this[propName].equals(object2[propName]))
                        return false;
        }
        else if (this[propName] instanceof Object && object2[propName] instanceof Object) {
                   // recurse into another objects
                   //console.log("Recursing to compare ", this[propName],"with",object2[propName], " both named \""+propName+"\"");
           if (!this[propName].equals(object2[propName]))
                        return false;
        }
        //Normal value comparison for strings and numbers
        else if(this[propName] != object2[propName]) {
           return false;
        }
    }
    //If everything passed, let's say YES
    return true;
}  

但是,请记住,这是用于比较 JSON 之类的数据,而不是类实例和其他东西。如果你想比较更复杂的对象,看看这个答案,它是超长函数
要使其与Array.equals一起使用,您必须稍微编辑原始函数:

...
    // Check if we have nested arrays
    if (this[i] instanceof Array && array[i] instanceof Array) {
        // recurse into the nested arrays
        if (!this[i].equals(array[i]))
            return false;
    }
    /**REQUIRES OBJECT COMPARE**/
    else if (this[i] instanceof Object && array[i] instanceof Object) {
        // recurse into another objects
        //console.log("Recursing to compare ", this[propName],"with",object2[propName], " both named \""+propName+"\"");
        if (!this[i].equals(array[i]))
            return false;
        }
    else if (this[i] != array[i]) {
...

为这两个功能做了一个小测试工具

奖励:带有indexOf和的嵌套数组contains

Samy Bencherif为您在嵌套数组中搜索特定对象的情况准备了有用的函数,可在此处获得:https : //jsfiddle.net/SamyBencherif/8352y6yw/

你的方法应该被调用equals而不是compare. 至少在 .NET 中,compare 通常返回一个有符号整数,指示哪个对象大于另一个。请参阅:Comperer.Compare
2021-02-06 23:15:00
此外,这不是关于它是否容易重写,而是关于答案不应该推荐被认为是不好的做法的东西(developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/...)并且应该绝对不要在标题“正确的方式”下方执行此操作
2021-02-14 23:15:00
如果要进行严格比较,请使用this[i] !== array[i]代替!=
2021-02-18 23:15:00
更改内置类型的原型绝对不是正确的方法
2021-02-25 23:15:00
这不仅是正确的做法,而且效率也更高。这是我为这个问题中建议的所有方法准备的一个快速 jsperf 脚本。jsperf.com/comparing-arrays2
2021-03-05 23:15:00

虽然这只适用于标量数组(见下面的注释),但它很短:

array1.length === array2.length && array1.every(function(value, index) { return value === array2[index]})

Rr,在带有箭头函数的 ECMAScript 6 / CoffeeScript / TypeScript 中:

array1.length === array2.length && array1.every((value, index) => value === array2[index])

(注意:这里的“标量”是指可以直接使用 进行比较的值===。所以:数字、字符串、引用对象、引用函数。有关比较运算符的更多信息,请参阅MDN 参考)。

更新

从我从评论中读到的,对数组进行排序和比较可能会给出准确的结果:

const array2Sorted = array2.slice().sort();
array1.length === array2.length && array1.slice().sort().every(function(value, index) {
    return value === array2Sorted[index];
});

例如:

array1 = [2,3,1,4];
array2 = [1,2,3,4];

然后上面的代码会给 true

它适用于任何类型的数组,无论是否排序@espertus
2021-02-10 23:15:00
@espertus 实际上,如果元素在两个数组中的顺序不完全相同,则不会返回 true。但是,相等性检查的目标不是检查它们是否包含相同的元素,而是检查它们是否具有相同顺序的相同元素。
2021-02-20 23:15:00
对,就是这样。这个函数应该比较两个数组,不管它们是否排序,它们的连续元素必须相等。
2021-02-22 23:15:00
如果您想检查两个数组都是平等的,含有相同的未分类的项目(但不包括多次使用),你可以使用a1.length==a2.length && a1.every((v,i)=>a2.includes(v))var a1 =[1,2,3], a2 = [3,2,1];var a1 =[1,3,3], a2 = [1,1,3];将无法按预期工作)
2021-02-22 23:15:00
我喜欢这个,尽管读者应该知道这仅适用于排序数组。
2021-03-06 23:15:00

我喜欢将 Underscore 库用于数组/对象繁重的编码项目……在 Underscore 和 Lodash 中,无论您是比较数组还是对象,它看起来都像这样:

_.isEqual(array1, array2)   // returns a boolean
_.isEqual(object1, object2) // returns a boolean
你也许可以使用 _.difference(); 如果订单对你来说无关紧要
2021-02-08 23:15:00
如果顺序无关紧要,我们可以在此检查之前对数组进行排序 _.isEqual([1,2,3].sort(), [2,1,3].sort()) => true
2021-02-14 23:15:00
或者如果你只想要isEqual功能,你总是可以使用 lodash.isequal module
2021-02-24 23:15:00
注意顺序很重要 _.isEqual([1,2,3], [2,1,3]) => false
2021-03-01 23:15:00
在 React.js 中抛出异常:'_' 未定义
2021-03-03 23:15:00

我认为这是使用 JSON stringify 最简单的方法,在某些情况下它可能是最好的解决方案:

JSON.stringify(a1) === JSON.stringify(a2);

这会将对象a1a2转换为字符串,以便可以比较它们。在大多数情况下,顺序很重要,因为它可以使用上述答案之一中显示的排序算法对对象进行排序。

请注意,您不再比较对象,而是比较对象的字符串表示。它可能不是您想要的。

很好的答案,但为什么 []==[] 返回 false ?两者都是简单的对象,那为什么?
2021-02-10 23:15:00
它更详细地说明了为什么这对于某些情况可能是一个很好的解决方案。
2021-02-18 23:15:00
该问题特别询问是否有比使用 JSON.stringify更好/更快的方法。
2021-02-23 23:15:00
只是要注意 JSON stringify 函数并不快。与较大的阵列一起使用肯定会引入滞后。
2021-02-26 23:15:00
@PardeepJain,这是因为默认情况下,ECMAScript for Objects 中的相等运算符在它们引用相同的内存位置时返回 true。试试 var x = y = []; // 现在相等返回 true。
2021-02-27 23:15:00

本着原始问题的精神:

我想比较两个数组...理想情况下,有效地没什么特别的,如果它们相同就为真,否则为假。

我一直在对这里提出的一些更简单的建议进行性能测试,结果如下(从快到慢):

(67%) by Tim Down

var i = a1.length;
while (i--) {
    if (a1[i] !== a2[i]) return false;
}
return true

每个(69%)由用户 2782196

a1.every((v,i)=> v === a2[i]);

通过 DEI减少(74%)

a1.reduce((a, b) => a && a2.includes(b), true);

加入& toString (78%)由 Gaizka Allende & vivek

a1.join('') === a2.join('');

a1.toString() === a2.toString();

half toString (90%) by Victor Palomo

a1 == a2.toString();

由 radtek字符串化(100%)

JSON.stringify(a1) === JSON.stringify(a2);

请注意,下面的示例假定数组是已排序的一维数组。.length已删除通用基准的比较(添加a1.length === a2.length到任何建议中,您将获得约 10% 的性能提升)。了解每种解决方案的速度和局限性,选择最适合您的解决方案。

不相关的说明:有趣的是,看到人们对这个问题的完全合法的答案投反对票,让所有触发快乐的约翰·韦恩斯(John Waynes)都投了反对票。

感谢您的性能测试:)
2021-02-08 23:15:00
join('')是危险的['foo', 'bar'].join('') == ['foobar'].join('')我更喜欢a1 ==''+ a2
2021-02-20 23:15:00
如果您增加数组大小,则这些数字不适用(尤其是 reduce 方法)。试试Array.from({length: 1000}).map((a,v)=> ${v}.padStart(10,2));
2021-02-21 23:15:00
该链接将打开一个空测试。
2021-02-28 23:15:00
应该使用sort()before a1& a2join。例如a1.sort().join("")===a2.sort().join("")
2021-03-03 23:15:00