Categories
arrays javascript json

How to compare arrays in JavaScript?

1392

I’d like to compare two arrays… ideally, efficiently. Nothing fancy, just true if they are identical, and false if not. Not surprisingly, the comparison operator doesn’t seem to work.

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 encoding each array does, but is there a faster or “better” way to simply compare arrays without having to iterate through each value?

14

  • 9

    You could first compare their length, and if they are equal each values.

    – TJHeuvel

    Oct 20, 2011 at 14:29

  • 71

    What makes two arrays equal for you? Same elements? Same order of elements? Encoding as JSON only works as long as the element of the array can be serialized to JSON. If the array can contain objects, how deep would you go? When are two objects “equal”?

    Oct 20, 2011 at 14:31


  • 80

    @FelixKling, defining “equality” is definitely a subtle topic, but for people coming to JavaScript from higher-level languages, there is no excuse for silliness like ([] == []) == false.

    – Alex D

    Mar 16, 2014 at 17:52

  • 7

    @AlexD it looks like arrays use reference equality which is what you’d expect. It’d be pretty awful if you couldn’t do that

    – JonnyRaa

    Aug 29, 2014 at 8:24

  • 6

    @AlexD I somewhat can’t think of a language where this doesn’t happen. In C++, you’d be comparing two pointers – false. In Java, you’re doing the same as in javascript. In PHP, something behind the scenes will loop through the arrays – do you call PHP a Higher level language?

    Aug 30, 2014 at 1:33

1030

To compare arrays, loop through them and compare every value:

Comparing arrays:

// 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});

Usage:

[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;

You may say “But it is much faster to compare strings – no loops…” well, then you should note there ARE loops. First recursive loop that converts Array to string and second, that compares two strings. So this method is faster than use of string.

I believe that larger amounts of data should be always stored in arrays, not in objects. However if you use objects, they can be partially compared too.
Here’s how:

Comparing objects:

I’ve stated above, that two object instances will never be equal, even if they contain same data at the moment:

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

This has a reason, since there may be, for example private variables within objects.

However, if you just use object structure to contain data, comparing is still possible:

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;
}  

However, remember that this one is to serve in comparing JSON like data, not class instances and other stuff. If you want to compare more complicated objects, look at this answer and it’s super long function.
To make this work with Array.equals you must edit the original function a little bit:

...
    // 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]) {
...

I made a little test tool for both of the functions.

Bonus: Nested arrays with indexOf and contains

Samy Bencherif has prepared useful functions for the case you’re searching for a specific object in nested arrays, which are available here: https://jsfiddle.net/SamyBencherif/8352y6yw/

52

  • 36

    If you want to do strict comparisons use this[i] !== array[i] instead of !=.

    – Tim S.

    May 15, 2013 at 13:19

  • 41

    Your method should be called equals instead of compare. At least in .NET, compare usually returns a signed int indicating which object is greater than the other. See: Comparer.Compare.

    – Oliver

    May 31, 2013 at 12:24

  • 17

    Nt only is this the right way of doing it, it’s also considerable more efficent. Here’s a quick jsperf script I prepared for all the methods suggested in this question. jsperf.com/comparing-arrays2

    – Tolga E

    Oct 16, 2013 at 19:30

  • 130

    Changing a built-in type’s prototype is definitely not the right way

    – Jasper

    Aug 14, 2014 at 9:29

  • 40

    Besides, it’s not about whether it’s easy to rewrite, it’s about the fact that an answer shouldn’t recommend something that’s considered bad practice (developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/…) and should definitely not do this below the header “The right way”

    – Jasper

    Sep 1, 2014 at 9:07

573

While this only works for scalar arrays (see note below), it is short code:

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

Same as above but in ECMAScript 6 / CoffeeScript / TypeScript with Arrow Functions:

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

(Note: ‘scalar’ here means values that can be compared directly using === . So: numbers, strings, objects by reference, functions by reference. See the MDN reference for more info about the comparison operators).

UPDATE

From what I read in the comments, sorting the array and comparing may give accurate result:

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

Eg:

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

Then the above code would return true

15

  • 23

    I like this, although readers should be aware this only works on sorted arrays.

    Dec 15, 2013 at 15:05

  • 21

    It works on any kind of arrays, sorted or not @espertus

    Mar 24, 2016 at 14:03

  • 48

    Yes, exactly. This function is supposed to compare two arrays, it doesn’t matter if they’re sorted or not, their consecutive elements have to be equal.

    Mar 26, 2016 at 0:39

  • 30

    @espertus Indeed, it won’t return true if the elements do not have the exact same order in both arrays. However, the goal of an equality check is not to check if they contains the same elements but to check if they have the same element in the same orders.

    Jun 6, 2016 at 3:25

  • 11

    If you want to check if both arrays are equals, containing the same unsorted items (but not used multiple times), you can use 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]; will not work as expected)

    – mems

    Nov 21, 2016 at 15:25

273

I like to use the Underscore library for array/object heavy coding projects … in Underscore and Lodash whether you’re comparing arrays or objects it just looks like this:

_.isEqual(array1, array2)   // returns a boolean
_.isEqual(object1, object2) // returns a boolean

6

  • 35

    Note that order matters _.isEqual([1,2,3], [2,1,3]) => false

    Aug 18, 2015 at 13:23


  • 3

    or if you want just the isEqual functionality, you can always use the lodash.isequal module

    – hellatan

    Feb 18, 2016 at 15:36

  • 9

    You can maybe use _.difference(); if order does not matter to you

    Mar 31, 2017 at 15:00

  • 14

    We can sort the array before this check if the order doesnt matter _.isEqual([1,2,3].sort(), [2,1,3].sort()) => true

    – filype

    Jun 2, 2017 at 11:30

  • in React.js throw an exception: ‘_’ is not defined

    May 14, 2020 at 10:13