1573

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?

17
  • 11
    You could first compare their length, and if they are equal each values. Commented Oct 20, 2011 at 14:29
  • 79
    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"? Commented Oct 20, 2011 at 14:31
  • 101
    @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. Commented Mar 16, 2014 at 17:52
  • 10
    @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 Commented Aug 29, 2014 at 8:24
  • 7
    @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? Commented Aug 30, 2014 at 1:33

57 Answers 57

1
2
3

Herer's my solution:

/**
 * Tests two data structures for equality
 * @param {object} x
 * @param {object} y
 * @returns {boolean}
 */
var equal = function(x, y) {
    if (typeof x !== typeof y) return false;
    if (x instanceof Array && y instanceof Array && x.length !== y.length) return false;
    if (typeof x === 'object') {
        for (var p in x) if (x.hasOwnProperty(p)) {
            if (typeof x[p] === 'function' && typeof y[p] === 'function') continue;
            if (x[p] instanceof Array && y[p] instanceof Array && x[p].length !== y[p].length) return false;
            if (typeof x[p] !== typeof y[p]) return false;
            if (typeof x[p] === 'object' && typeof y[p] === 'object') { if (!equal(x[p], y[p])) return false; } else
            if (x[p] !== y[p]) return false;
        }
    } else return x === y;
    return true;
};

Works with any nested data structure, and obviously ignores objects' methods. Don't even think of extending Object.prototype with this method, when I tried this once, jQuery broke ;)

For most arrays it's still faster than most of serialization solutions. It's probably the fastest compare method for arrays of object records.

Sign up to request clarification or add additional context in comments.

1 Comment

no good! these give true: equal({}, {a:1}) and equal({}, null) and this errors out: equal({a:2}, null)
3
JSON.stringify(collectionNames).includes(JSON.stringify(sourceNames)) ?  array.push(collection[i]) : null

This is how i did it.

1 Comment

Good solution - but I wonder in certain situations if it will not always work as intended, such as with certain primitives or deeply nested arrays? I hope it works in all circumstances though
3

An alternative way using filter and arrow functions

arrOne.length === arrTwo.length && arrOne.filter((currVal, idx) => currVal !== arrTwo[idx]).length === 0

Comments

3

I know that JSON.stringfy is slow when dealing with large datasets but what if you used template literals?

Example:

const a = [1, 2, 3];
const b = [1, 2, 'test'];

const a_string = Array.isArray(a) && `${a}`;
const b_string = Array.isArray(b) && `${b}`;

const result = (a === b);

console.log(result);

Taking into consideration you're using ES6 of course.

=)

5 Comments

Beware, this comparison will also work for an array compared to a string.
@EliasDorneles, Thanks a bunch! I updated my answer to perform a check before assigning the variable to a template string literals.
Hehe, now it will say these two are equal a = ["1,1", "2"] and b = [1,1,2] (or b = ["1", "1,2"])... I think it will be hard to get something robust with this approach. ^^
Javascript is a loosely typed language :). So in my opinion, if that was the case then I'd opt for something like typescript or flow. XD
This also doesn't work reliably when the array contains objects, where OTOH JSON.stringify won't perform a shallow comparison either.
3

It's a tricky implicit array equality checking but can handle the job right after coherence arrays to string.

var a1 = [1, 2, 3];
var a2 = [1, 2, 3];
var isEqual = a1 <= a2 && a1 >= a2; // true

Comments

2

Extending Tomáš Zato idea. Tomas's Array.prototype.compare should be infact called Array.prototype.compareIdentical.

It passes on:

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

But fails on:

[[1, 2, [3, 2]],1, 2, [3, 2]].compareIdentical([1, 2, [3, 2],[1, 2, [3, 2]]])

Here is better (in my opinion) version:

Array.prototype.compare = 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;

    this.sort();
    array.sort();
    for (var i = 0; i < this.length; i++) {
        // Check if we have nested arrays
        if (this[i] instanceof Array && array[i] instanceof Array) {
            // recurse into the nested arrays
            if (!this[i].compare(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;
}

http://jsfiddle.net/igos/bcfCY/

2 Comments

-1. If it 'fails' on the example you've given, then that's only the case for a somewhat arbitrary definition of 'fails'. Why would you expect those two different arrays to be considered equal? You haven't even explained what concept of 'equality' you're trying to implement here, or why it's a sensible or helpful one, but it looks like you want multidimensional arrays to be compared as if they were collapsed down to single-dimensional ones. If so, you didn't even achieve that: [1,2].compare([[1,2]]) gives false with your version, just as with Tomáš's.
Based on what I could infer, he's saying that [1, 2, 3, 4] and [1, 3, 2, 4] should be compared as equal (Order doesn't matter).
2
var a1 = [1,2,3,6];
var a2 = [1,2,3,5];

function check(a, b) {
  return (a.length != b.length) ? false : 
  a.every(function(row, index) {
    return a[index] == b[index];
  });
}  

check(a1, a2);

////// OR ///////

var a1 = [1,2,3,6];
var a2 = [1,2,3,6];

function check(a, b) {
  return (a.length != b.length) ? false : 
  !(a.some(function(row, index) {
    return a[index] != b[index];
  }));
}  

check(a1, a2)

1 Comment

You can you some function as well which will not iterate completely if we get the required condition satisfied, like above
2

Recursive & works on NESTED arrays:

function ArrEQ(a1,a2){
   return( 
        //:Are both elements arrays?
        Array.isArray(a1)&&Array.isArray(a2) 
        ?
        //:Yes: Test each entry for equality:
        a1.every((v,i)=>(ArrEQ(v,a2[i])))
        :
        //:No: Simple Comparison:
        (a1===a2)
   );;
};;

console.log( "Works With Nested Arrays:" );
console.log( ArrEQ( 
    [1,2,3,[4,5,[6,"SAME/IDENTICAL"]]],
    [1,2,3,[4,5,[6,"SAME/IDENTICAL"]]]
));;     
console.log( ArrEQ( 
    [1,2,3,[4,5,[6,"DIFFERENT:APPLES" ]]],
    [1,2,3,[4,5,[6,"DIFFERENT:ORANGES"]]]
));;  

Comments

2

Works with MULTIPLE arguments with NESTED arrays:

//:Return true if all of the arrays equal.
//:Works with nested arrays.
function AllArrEQ(...arrays){
    for(var i = 0; i < (arrays.length-1); i++ ){
        var a1 = arrays[i+0];
        var a2 = arrays[i+1];
        var res =( 
            //:Are both elements arrays?
            Array.isArray(a1)&&Array.isArray(a2) 
            ?
            //:Yes: Compare Each Sub-Array:
            //:v==a1[i]
            a1.every((v,i)=>(AllArrEQ(v,a2[i])))
            :
            //:No: Simple Comparison:
            (a1===a2)
        );;
        if(!res){return false;}
    };;
    return( true );
};;

console.log( AllArrEQ( 
        [1,2,3,[4,5,[6,"ALL_EQUAL"   ]]],
        [1,2,3,[4,5,[6,"ALL_EQUAL"   ]]],
        [1,2,3,[4,5,[6,"ALL_EQUAL"   ]]],
        [1,2,3,[4,5,[6,"ALL_EQUAL"   ]]],
));; 

Comments

2

I believe in plain JS and with ECMAScript 2015, which is sweet and simple to understand.

var is_arrays_compare_similar = function (array1, array2) {

    let flag = true;

    if (array1.length == array2.length) {

        // check first array1 object is available in array2 index
        array1.every( array_obj => {
            if (flag) {
                if (!array2.includes(array_obj)) {
                    flag = false;
                }
            }
        });
        
        // then vice versa check array2 object is available in array1 index
        array2.every( array_obj => {
            if (flag) {
                if (!array1.includes(array_obj)) {
                    flag = false;
                }
            }
        });

        return flag;
    } else {
        return false;
    }
    
}

2 Comments

Why is the vice versa check needed? We know the arrays are the same size, so if every item in array1 is also found in array2; why would we then have to check that every item in array2 is also in array1?
First array could be [1,1] second [1,2] without vice versa this cannot be detected
2

Surprisingly, nobody brought up a solution with find

const a = [1, 2, 3]
const b = [1, 2, 3, 4]
a.find((v,i) => v !== b[i])

The benefit here is that instead of comparing all values it looks for the first occurrence and ends the loop as early as possible. Or in other words, instead of asking "are two arrays equal?" it asks "is one array different from another?".

3rd by performance benchmark https://jsben.ch/TgFrA

Keep in mind, the order matters, a.find(...) !== b.find(...) and could be checked by a.length === b.length

if (a.length === b.length && a.find((v,i) => v !== b[i]) === undefined) {
  // equal
}

1 Comment

a.find((v,i) => v !== b[i]) is exactly equivalent to a.some((v,i) => v !== b[i]) with the difference that .some() returns a boolean directly. .some() and .every() are also equivalent to each other through negation: a.every((v,i) => v -== b[i]). When I ran the benchmark the .every() code was in second place in front of the .find() variant.
1

Actually, in the Lodash documentation, they give two pretty good examples for comparing and return fresh arrays for both differences and similarities (respectively in the examples below):

import { differenceWith, intersectionWith, isEqual } from 'lodash'

differenceWith(
  [{ a: 1 }, { b: 1 }],
  [{ a: 1 }, { b: 1 }, { c: 1 }],
  isEqual
) // []... 💀the bigger array needs to go first!

differenceWith(
  [{ a: 1 }, { b: 1 }, { c: 1 }],
  [{ a: 1 }, { b: 1 }],
  isEqual,
) // [{ c: 1 }] 🎉

intersectionWith(
  [{ a: 1 }, { b: 1 }],
  [{ a: 1 }, { b: 1 }, { c: 1 }],
  isEqual,
) // [{ a: 1 }, { b: 1 }] 🎉this one doesn't care about which is bigger

If you won't always know which array will be bigger, you can write a helper function for it like so:

const biggerFirst = (arr1, arr2) => {
  return arr1.length > arr2.length ? [arr1, arr2] : [arr2, arr1]
}

const [big, small] = biggerFirst(
  [{ a: 1 }, { b: 1 }],
  [{ a: 1 }, { b: 1 }, { c: 1 }],
)

differenceWith(big, small, isEqual) // 🎉even though we have no idea which is bigger when they are fed to biggerFirst()

From what I can tell, these match deeply as well so that's pretty nice.

I know relying on libraries for everything shouldn't be applauded, but this is the most concise/clean solution I've found to a really common problem. Hope it helps someone!

Comments

1

All the other solutions look complicated. This might not be the most efficient with or handle all edge cases but it works great for me.

Array.prototype.includesArray = function(arr) {
  return this.map(i => JSON.stringify(i)).includes(JSON.stringify(arr))
}

Usage

[[1,1]].includesArray([1,1])
// true

[[1,1]].includesArray([1,1,2])
// false

1 Comment

This is very inneficient, and will produce an arror if any of these arrays contains a recursive structure or something otherwise non-serializable.
0

this script compares Object, Arrays and multidimensional array

function compare(a,b){
     var primitive=['string','number','boolean'];
     if(primitive.indexOf(typeof a)!==-1 && primitive.indexOf(typeof a)===primitive.indexOf(typeof b))return a===b;
     if(typeof a!==typeof b || a.length!==b.length)return false;
     for(i in a){
          if(!compare(a[i],b[i]))return false;
     }
     return true;
}

first line checks whether it's a primitive type. if so it compares the two parameters.

if they are Objects. it iterates over the Object and check every element recursivly.

Usage:

var a=[1,2,[1,2]];
var b=[1,2,[1,2]];
var isEqual=compare(a,b);  //true

Comments

0

This function compares two arrays of arbitrary shape and dimesionality:

function equals(a1, a2) {

    if (!Array.isArray(a1) || !Array.isArray(a2)) {
        throw new Error("Arguments to function equals(a1, a2) must be arrays.");
    }

    if (a1.length !== a2.length) {
        return false;
    }

    for (var i=0; i<a1.length; i++) {
        if (Array.isArray(a1[i]) && Array.isArray(a2[i])) {
            if (equals(a1[i], a2[i])) {
                continue;
            } else {
                return false;
            }
        } else {
            if (a1[i] !== a2[i]) {
                return false;
            }
        }
    }

    return true;
}

Comments

0

With an option to compare the order or not:

function arraysEqual(a1, a2, compareOrder) {
    if (a1.length !== a2.length) {
        return false;
    }

    return a1.every(function(value, index) {
        if (compareOrder) {
            return value === a2[index];
        } else {
            return a2.indexOf(value) > -1;
        }
    });
}

Comments

0

Récursive cmp function working with number/string/array/object

<script>
var cmp = function(element, target){

   if(typeof element !== typeof target)
   {
      return false;
   }
   else if(typeof element === "object" && (!target || !element))
   {
      return target === element;
   }
   else if(typeof element === "object")
   {
       var keys_element = Object.keys(element);
       var keys_target  = Object.keys(target);
       
       if(keys_element.length !== keys_target.length)
       {
           return false;
       }
       else
       {
           for(var i = 0; i < keys_element.length; i++)
           {
                if(keys_element[i] !== keys_target[i])
                    return false;
                if(!cmp(element[keys_element[i]], target[keys_target[i]]))
                    return false;
           }
		   return true;
       }
   }
   else
   {
   	   return element === target;

   }
};

console.log(cmp({
    key1: 3,
    key2: "string",
    key3: [4, "45", {key4: [5, "6", false, null, {v:1}]}]
}, {
    key1: 3,
    key2: "string",
    key3: [4, "45", {key4: [5, "6", false, null, {v:1}]}]
})); // true

console.log(cmp({
    key1: 3,
    key2: "string",
    key3: [4, "45", {key4: [5, "6", false, null, {v:1}]}]
}, {
    key1: 3,
    key2: "string",
    key3: [4, "45", {key4: [5, "6", undefined, null, {v:1}]}]
})); // false
</script>

Comments

0

This method is one that only works on scalar arrays, like the second voted answer on this question.

var arrs = [
  [[1, 2, 3], [1, 2, 3]], // true
  [[1, 2, 3, 4], [1, 2, 3]], // false
  [[1, 2, 3], [1, 2, 3, 4]], // false
]

const arraysEqual = (one, two) => (one.filter((i, n) => two[n] === i).length === one.length) && (two.filter((i, n) => one[n] === i).length === two.length)

arrs.forEach(arr => {
  console.log(arraysEqual(arr[0], arr[1]))
})

Without ES6 syntax:

var arrs = [
  [[1, 2, 3], [1, 2, 3]], // true
  [[1, 2, 3, 4], [1, 2, 3]], // false
  [[1, 2, 3], [1, 2, 3, 4]], // false
]

function arraysEqual(one, two) {
  return (one.filter((i, n) => two[n] === i).length === one.length) && (two.filter((i, n) => one[n] === i).length === two.length)
}

arrs.forEach(arr => {
  console.log(arraysEqual(arr[0], arr[1]))
})

Comments

0

I answered this question at https://stackoverflow.com/a/10316616/711085 (which has since been marked a duplicate of this answer). There you will find a Deep Equals implementation that handles numerous cases, such as Map and Set and arbitrary nesting of arrays and objects. The discussion therein of non-transitivity of == and documenting == vs === is particularly important.


For OP's particular problem, if the arrays consist only of numbers and strings and booleans, and no NaNs, then the most efficient method for sufficiently large arrays is a precompiled function:

function areSimpleArraysEqual(a,b) {
    // requires inputs be arrays of only Number, String, Boolean, and no NaN.
    // will propagate error if either array is undefined.
    if (a.length!=b.length)
        return false;
    for(let i=0; i<a.length; i++)
        if (a[i]!==b[i]) // using === equality
            return false;
    return true;
}

One may achieve average-case O(1) and worst-case O(N) in some rare instances if one's business logic keeps appending to the ends of the arrays, by also checking if (a.length>0 && a[a.length-1]!==b[b.length-1]) return false; .

1 Comment

I find it unbelievable that such a widely used language is missing such an essential operation. Python and some other languages are more sane (a==b is true if a and b are arrays with the same elements in the same order). Identity is a different question (a is b in Python). BTW, this thread is conflating lists and sets. Sets are equal if they have the same elements, but for a list/array the elements must be in the same order for equality.
0

If you like simple savagery:

const a1 = ['a', 'b', 'e', 'd', 'c'];
const a2 = ['a', 'b', 'c', 'd', 'e'];

a1.length === a2.length && [...a1, ...a2].filter((item) => a2.indexOf(item) === -1).length === 0

Note that you can perform "indexOf" on any of the 2 arrays.

2 Comments

I guess I get downvotes because this solution does not assert keys are ordered in the same fashion. OP did not specify it precisely, I guess I could have derived it from the term "identical". Nonetheless, this solution proves to be very useful, for an array of "primitives", and is quite efficient compared to other solutions.
It may also be that indexOf(item) === -1 fell out of favor when ! includes(item) was introduced in 2016.
-1

If the array is plain and the order is matter so this two lines may help

//Assume
var a = ['a','b', 'c']; var b = ['a','e', 'c'];  

if(a.length !== b.length) return false;
return !a.reduce(
  function(prev,next,idx, arr){ return prev || next != b[idx] },false
); 

Reduce walks through one of array and returns 'false' if at least one element of 'a' is nor equial to element of 'b' Just wrap this into function

3 Comments

map, reduce, filter everything! :P
This is a bad solution because Array.prototype.reduce will walk through every element in a even if the first compared elements do not match. Also using !a and != in the loop is a double negative which makes this answer more complicated (and hard to read) than it needs to be
Agree. There is some some() function :) Two years ago i didn't know it. But double negation still will be there.
-1

tried deep-equal and it worked

var eq = require('deep-equal');
eq({a: 1, b: 2, c: [3, 4]}, {c: [3, 4], a: 1, b: 2});

Comments

-1
let equals = (LHS, RHS) => {
    if (!(LHS instanceof Array)) return "false > L.H.S is't an array";
    if (!(RHS instanceof Array)) return "false > R.H.S is't an array";
    if (LHS.length != RHS.length) return false;
    let to_string = x => JSON.stringify(x.sort((a, b) => a - b));
    return to_string(LHS) == to_string(RHS);
  };

let l = console.log
l(equals([5,3,2],[3,2,5]))    // true
l(equals([3,2,5,3],[3,2,5]))  // false

Comments

-1

My solution compares Objects, not Arrays. This would work in the same way as Tomáš's as Arrays are Objects, but without the Warning:

Object.prototype.compare_to = function(comparable){
    
    // Is the value being compared an object
    if(comparable instanceof Object){
        
        // Count the amount of properties in @comparable
        var count_of_comparable = 0;
        for(p in comparable) count_of_comparable++;
        
        // Loop through all the properties in @this
        for(property in this){
            
            // Decrements once for every property in @this
            count_of_comparable--;
            
            // Prevents an infinite loop
            if(property != "compare_to"){
                
                // Is the property in @comparable
                if(property in comparable){
                    
                    // Is the property also an Object
                    if(this[property] instanceof Object){
                        
                        // Compare the properties if yes
                        if(!(this[property].compare_to(comparable[property]))){
                            
                            // Return false if the Object properties don't match
                            return false;
                        }
                    // Are the values unequal
                    } else if(this[property] !== comparable[property]){
                        
                        // Return false if they are unequal
                        return false;
                    }
                } else {
                
                    // Return false if the property is not in the object being compared
                    return false;
                }
            }
        }
    } else {
        
        // Return false if the value is anything other than an object
        return false;
    }
    
    // Return true if their are as many properties in the comparable object as @this
    return count_of_comparable == 0;
}

Comments

-1

vary short and useful .. you know length are equal and arrays are simple

const imageData = [1,2,3,4] ;
const imageData2 = [1,2,3,4] ;

// true = not equal
return imageData.find((x, i) => imageData2[i] != x)
// or true = equal
return imageData.every((x, i) => imageData2[i] == x)

and when you do not know that

return JSON.stringify(imageData) == JSON.stringify(imageData2) 

Comments

-2

Here's a CoffeeScript version, for those who prefer that:

Array.prototype.equals = (array) ->
  return false if not array # if the other array is a falsy value, return
  return false if @length isnt array.length # compare lengths - can save a lot of time

  for item, index in @
    if item instanceof Array and array[index] instanceof Array # Check if we have nested arrays
      if not item.equals(array[index]) # recurse into the nested arrays
        return false
    else if this[index] != array[index]
      return false # Warning - two different object instances will never be equal: {x:20} != {x:20}
  true

All credits goes to @tomas-zato.

Comments

-4

I would do like this:

[2,3,4,5] == [2,3,4,5].toString()

When you use the "==" operator, JavaScript checks if the values(left and right) is the same type, if it's different JavaScript try to convert both side in the same type.

Array == String

Array has toString method so JavaScript uses it to convert them to the same type, work the same way writing like this:

[2,3,4,5].toString() == [2,3,4,5].toString()

3 Comments

Oversight: [1,2,3,4].toString() === ["1,2,3",4].toString() // => true
It's better to use it when you have only one level of deepness
that's one level of deepness
1
2

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.