How to compare arrays in JavaScript

How to compare arrays in JavaScript

array.jpg

array1 === array2 ?

To compare arrays in JavaScript, don't assume you can check whether the contents of two JavaScript arrays are the same with === or == (the strict equality or equality operators). You can't.

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

array1 === array2    // returns false

The code above is checking if the above two variables refer to the same array instance, and not whether their contents are the same. It would only return true for something like this:

const array1 = [1, 2, 3]
const array2 = array1;     // both variables point to the same array instance

array1 === array2  // now returns true

Solutions

JSON.stringify()

One way to solve the problem is to use JSON.stringify(), which converts a value to a JSON string.

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

JSON.stringify(array1) === JSON.stringify(array2)   // returns true

JSON.stringify even works with multiple levels of nesting:

const array1 = [1, 2, 3, {x: 5}, [1, {y: 7}, 3]];
const array2 = [1, 2, 3, {x: 5}, [1, {y: 7}, 3]];

JSON.stringify(array1) === JSON.stringify(array2);    // returns true

However, this method is not perfect. It is dependent on the JSON.stringify() method implementation not changing. There are also edge cases that produce strange behavior. Undefined is not a valid JSON value, so JSON.stringify() converts undefined to null, and this method could falsely return true in that case:

const array1 = [null, 2, 3];
const array2 = [undefined, 2, 3];


JSON.stringify(array1) === JSON.stringify(array2)
  // returns true, should be false

null === undefined     
  // returns false

array.every()

Another method uses array.every(). It first checks if the arrays are the same length, and then checks that each value in the first equals the value in the second at the same index:

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

console.log(isEqual);

However, the array.every() way won't work for arrays nested with additional arrays or objects, which would need to be checked recursively.

Recursive methods

This can be done with the isEqual method from the Lodash library. According to this gist, it is doing something under the hood like the following:

const array1 = [1, [1, [{a: 'b'}]], 3];
const array2 = [1, [1, [{a: 'b'}]], 3];

const isEqual = (first, second) => {
  if (first === second) {
    return true;
  }
  if ((first === undefined || second === undefined 
|| first === null || second === null) && (first || second)) {
    return false;
  }
  const firstType = first?.constructor.name;
  const secondType = second?.constructor.name;
  if (firstType !== secondType) {
    return false;
  }
  if (firstType === 'Array') {
    if (first.length !== second.length) {
      return false;
    }
    let equal = true;
    for (let i = 0; i < first.length; i++) {
      if (!isEqual(first[i], second[i])) {
        equal = false;
        break;
      }
    }
    return equal;
  }
  if (firstType === 'Object') {
    let equal = true;
    const fKeys = Object.keys(first);
    const sKeys = Object.keys(second);
    if (fKeys.length !== sKeys.length) {
      return false;
    }
    for (let i = 0; i < fKeys.length; i++) {
      if (first[fKeys[i]] && second[fKeys[i]]) {
        if (first[fKeys[i]] === second[fKeys[i]]) {
          continue; // eslint-disable-line
        }
        if (first[fKeys[i]] && (first[fKeys[i]].constructor.name === 'Array'
          || first[fKeys[i]].constructor.name === 'Object')) {
          equal = isEqual(first[fKeys[i]], second[fKeys[i]]);
          if (!equal) {
            break;
          }
        } else if (first[fKeys[i]] !== second[fKeys[i]]) {
          equal = false;
          break;
        }
      } else if ((first[fKeys[i]] && !second[fKeys[i]]) || 
          (!first[fKeys[i]] && second[fKeys[i]])) {
        equal = false;
        break;
      }
    }
    return equal;
  }
  return first === second;
};

console.log(isEqual(array1, array2));    // returns true

References