How to compare arrays in JavaScript?

How to compare arrays in JavaScript?

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?

Solutions/Answers:

Solution 1:

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 mor complicated objects, look at this answer and it’s superlong 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/

Solution 2:

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

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

Rr, 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 from the comments, sorting the array and comparing may give accurate result:

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

Eg:

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

Then the above code would give true

Solution 3:

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

Solution 4:

This I think is the simplest way to do it using JSON stringify, and it may be the best solution in some situations:

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

This converts the objects a1 and a2 into strings so they can be compared. The order is important in most cases, for that can sort the object using a sort algorithm shown in one of the above answers.

Please do note that you are no longer comparing the object but the string representation of the object. It may not be exactly what you want.

Solution 5:

It’s unclear what you mean by “identical”. For example, are the arrays a and b below identical (note the nested arrays)?

var a = ["foo", ["bar"]], b = ["foo", ["bar"]];

Here’s an optimized array comparison function that compares corresponding elements of each array in turn using strict equality and does not do recursive comparison of array elements that are themselves arrays, meaning that for the above example, arraysIdentical(a, b) would return false. It works in the general case, which JSON- and join()-based solutions will not:

function arraysIdentical(a, b) {
    var i = a.length;
    if (i != b.length) return false;
    while (i--) {
        if (a[i] !== b[i]) return false;
    }
    return true;
};

Solution 6:

The Practical Way

I think it’s wrong to say a particular implementation is “The Right Way™” if it’s only “right” (“correct”) in contrast to a “wrong” solution. Tomáš’s solution is a clear improvement over string-based array comparison, but that doesn’t mean it’s objectively “right”. What is right anyway? Is it the fastest? Is it the most flexible? Is it the easiest to comprehend? Is it the quickest to debug? Does it use the least operations? Does it have any side effects? No one solution can have the best of all the things.

Tomáš’s could say his solution is fast but I would also say it is needlessly complicated. It tries to be an all-in-one solution that works for all arrays, nested or not. In fact, it even accepts more than just arrays as an input and still attempts to give a “valid” answer.


Generics offer reusability

My answer will approach the problem differently. I’ll start with a generic arrayCompare procedure that is only concerned with stepping through the arrays. From there, we’ll build our other basic comparison functions like arrayEqual and arrayDeepEqual, etc

// arrayCompare :: (a -> a -> Bool) -> [a] -> [a] -> Bool
const arrayCompare = f => ([x,...xs]) => ([y,...ys]) =>
  x === undefined && y === undefined
    ? true
    : Boolean (f (x) (y)) && arrayCompare (f) (xs) (ys)

In my opinion, the best kind of code doesn’t even need comments, and this is no exception. There’s so little happening here that you can understand the behaviour of this procedure with almost no effort at all. Sure, some of the ES6 syntax might seem foreign to you now, but that’s only because ES6 is relatively new.

As the type suggests, arrayCompare takes comparison function, f, and two input arrays, xs and ys. For the most part, all we do is call f (x) (y) for each element in the input arrays. We return an early false if the user-defined f returns false – thanks to &&‘s short-circuit evaluation. So yes, this means the comparator can stop iteration early and prevent looping through the rest of the input array when unnecessary.


Strict comparison

Next, using our arrayCompare function, we can easily create other functions we might need. We’ll start with the elementary arrayEqual

// equal :: a -> a -> Bool
const equal = x => y =>
  x === y // notice: triple equal

// arrayEqual :: [a] -> [a] -> Bool
const arrayEqual =
  arrayCompare (equal)

const xs = [1,2,3]
const ys = [1,2,3]
console.log (arrayEqual (xs) (ys))      //=> true
// (1 === 1) && (2 === 2) && (3 === 3)  //=> true

const zs = ['1','2','3']
console.log (arrayEqual (xs) (zs))      //=> false
// (1 === '1')                          //=> false

Simple as that. arrayEqual can be defined with arrayCompare and a comparator function that compares a to b using === (for strict equality).

Notice that we also define equal as it’s own function. This highlights the role of arrayCompare as a higher-order function to utilize our first order comparator in the context of another data type (Array).


Loose comparison

We could just as easily defined arrayLooseEqual using a == instead. Now when comparing 1 (Number) to '1' (String), the result will be true

// looseEqual :: a -> a -> Bool
const looseEqual = x => y =>
  x == y // notice: double equal

// arrayLooseEqual :: [a] -> [a] -> Bool
const arrayLooseEqual =
  arrayCompare (looseEqual)

const xs = [1,2,3]
const ys = ['1','2','3']
console.log (arrayLooseEqual (xs) (ys))    //=> true
// (1 == '1') && (2 == '2') && (3 == '3')  //=> true

Deep comparison (recursive)

You’ve probably noticed that this is only shallow comparison tho. Surely Tomáš’s solution is “The Right Way™” because it does implicit deep comparison, right ?

Well our arrayCompare procedure is versatile enough to use in a way that makes a deep equality test a breeze …

// isArray :: a -> Bool
const isArray =
  Array.isArray

// arrayDeepCompare :: (a -> a -> Bool) -> [a] -> [a] -> Bool
const arrayDeepCompare = f =>
  arrayCompare (a => b =>
    isArray (a) && isArray (b)
      ? arrayDeepCompare (f) (a) (b)
      : f (a) (b))

const xs = [1,[2,[3]]]
const ys = [1,[2,['3']]]
console.log (arrayDeepCompare (equal) (xs) (ys)) //=> false
// (1 === 1) && (2 === 2) && (3 === '3')         //=> false

console.log (arrayDeepCompare (looseEqual) (xs) (ys)) //=> true
// (1 == 1) && (2 == 2) && (3 == '3')                 //=> true

Simple as that. We build a deep comparator using another higher-order function. This time we’re wrapping arrayCompare using a custom comparator that will check if a and b are arrays. If so, reapply arrayDeepCompare otherwise compare a and b to the user-specified comparator (f). This allows us to keep the deep comparison behavior separate from how we actually compare the individual elements. Ie, like the example above shows, we can deep compare using equal, looseEqual, or any other comparator we make.

Because arrayDeepCompare is curried, we can partially apply it like we did in the previous examples too

// arrayDeepEqual :: [a] -> [a] -> Bool
const arrayDeepEqual =
  arrayDeepCompare (equal)

// arrayDeepLooseEqual :: [a] -> [a] -> Bool
const arrayDeepLooseEqual =
  arrayDeepCompare (looseEqual)

To me, this already a clear improvement over Tomáš’s solution because I can explicitly choose a shallow or deep comparison for my arrays, as needed.


Object comparison (example)

Now what if you have an array of objects or something ? Maybe you want to consider those arrays as “equal” if each object has the same id value …

// idEqual :: {id: Number} -> {id: Number} -> Bool
const idEqual = x => y =>
  x.id !== undefined && x.id === y.id

// arrayIdEqual :: [a] -> [a] -> Bool
const arrayIdEqual =
  arrayCompare (idEqual)

const xs = [{id:1}, {id:2}]
const ys = [{id:1}, {id:2}]
console.log (arrayIdEqual (xs) (ys)) //=> true
// (1 === 1) && (2 === 2)            //=> true

const zs = [{id:1}, {id:6}]
console.log (arrayIdEqual (xs) (zs)) //=> false
// (1 === 1) && (2 === 6)            //=> false

Simple as that. Here I’ve used vanilla JS objects, but this type of comparator could work for any object type; even your custom objects. Tomáš’s solution would need to be completely reworked to support this kind of equality test

Deep array with objects? Not a problem. We built highly versatile, generic functions, so they’ll work in a wide variety of use cases.

const xs = [{id:1}, [{id:2}]]
const ys = [{id:1}, [{id:2}]]
console.log (arrayCompare (idEqual) (xs) (ys))     //=> false
console.log (arrayDeepCompare (idEqual) (xs) (ys)) //=> true

Arbitrary comparison (example)

Or what if you wanted to do some other kind of kind of completely arbitrary comparison ? Maybe I want to know if each x is greater than each y

// gt :: Number -> Number -> Bool
const gt = x => y =>
  x > y

// arrayGt :: [a] -> [a] -> Bool
const arrayGt = arrayCompare (gt)

const xs = [5,10,20]
const ys = [2,4,8]
console.log (arrayGt (xs) (ys))     //=> true
// (5 > 2) && (10 > 4) && (20 > 8)  //=> true

const zs = [6,12,24]
console.log (arrayGt (xs) (zs))     //=> false
// (5 > 6)                          //=> false

Less is More

You can see we’re actually doing more with less code. There’s nothing complicated about arrayCompare itself and each of the custom comparators we’ve made have a very simple implementation.

With ease, we can define exactly how we wish for two arrays to be compared — shallow, deep, strict, loose, some object property, or some arbitrary computation, or any combination of these — all using one procedure, arrayCompare. Maybe even dream up a RegExp comparator ! I know how kids love those regexps …

Is it the fastest? Nope. But it probably doesn’t need to be either. If speed is the only metric used to measure the quality of our code, a lot of really great code would get thrown away — That’s why I’m calling this approach The Practical Way. Or maybe to be more fair, A Practical Way. This description is suitable for this answer because I’m not saying this answer is only practical in comparison to some other answer; it is objectively true. We’ve attained a high degree of practicality with very little code that’s very easy to reason about. No other code can say we haven’t earned this description.

Does that make it the “right” solution for you ? That’s up for you to decide. And no one else can do that for you; only you know what your needs are. In almost all cases, I value straightforward, practical, and versatile code over clever and fast kind. What you value might differ, so pick what works for you.


Edit

My old answer was more focused on decomposing arrayEqual into tiny procedures. It’s an interesting exercise, but not really the best (most practical) way to approach this problem. If you’re interested, you can see this revision history.