DEV Community

Cover image for How to Compare Same Contents but Different Ordered Arrays in JavaScript?
Thanga Ganapathy
Thanga Ganapathy

Posted on

How to Compare Same Contents but Different Ordered Arrays in JavaScript?

Welcome,

One common challenge developers face is comparing arrays—specifically, determining if two arrays contain the same contents, regardless of the order of those contents. This task might seem straightforward at first glance, but it quickly delves into complexities.

In this blog post, we’ll discuss various methods, from the simple and direct to the more sophisticated and efficient, ensuring that by the end of this article.

Basics

This is for Beginners, in JavaScript, comparing two arrays with equality operators simply do not work.

[] == [] // false
[] === [] // false
Enter fullscreen mode Exit fullscreen mode

Comparing Two Arrays

Let us begin with regular array comparisons.

function arraysEqual(a, b) {
  if (a === b) return true;
  if (a == null || b == null) return false;
  if (a.length !== b.length) return false;

  for (let i = 0; i < a.length; i++) {
    if (a[i] !== b[i]) return false;
  }

  return true;
}

// Example usage:
const array1 = [1, 2, 3];
const array2 = [1, 2, 3];
const array3 = [1, 2, 4];

arraysEqual(array1, array2); // true
arraysEqual(array1, array3); // false
Enter fullscreen mode Exit fullscreen mode

Level 1

Our arraysEqual function works for regular array comparisons but not for different ordered.

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

arraysEqual(array1, array2); // false
Enter fullscreen mode Exit fullscreen mode

Let's change it to work for different ordered arrays.

We can do this by simply sort the given arrays before comparison.

function arraysEqual(a, b) {
  a.sort();
  b.sort();

  if (a === b) return true;
  if (a == null || b == null) return false;
  if (a.length !== b.length) return false;

  for (let i = 0; i < a.length; i++) {
    if (a[i] !== b[i]) return false;
  }

  return true;
}
Enter fullscreen mode Exit fullscreen mode

Now our function works well with different ordered arrays.

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

arraysEqual(array1, array2); // true
Enter fullscreen mode Exit fullscreen mode

Level 2

Now we test the arraysEqual function by giving mixed type of values.

arraysEqual(['1', 1], [1, '1']); // false
Enter fullscreen mode Exit fullscreen mode

As you can see our function failed even with the two arrays contain same values.

Let's fix this.

function compareMixedTypes(a, b) {
  if (typeof a === typeof b) {
    return a === b ? 0 : a < b ? -1 : 1;
  }
  return typeof a < typeof b ? -1 : 1;
}

function arraysEqual(a, b) {
  a.sort(compareMixedTypes);
  b.sort(compareMixedTypes);

  if (a === b) return true;
  if (a == null || b == null) return false;
  if (a.length !== b.length) return false;

  for (let i = 0; i < a.length; i++) {
    if (a[i] !== b[i]) return false;
  }

  return true;
}
Enter fullscreen mode Exit fullscreen mode
arraysEqual(['1', 1], [1, '1']); // true
Enter fullscreen mode Exit fullscreen mode

Level 3

Let's test the arraysEqual function with array of objects.

const a = [{a: 1}, {b: 2}];
const b = [{b: 2}, {a: 1}];

arraysEqual(a, b); // false
Enter fullscreen mode Exit fullscreen mode

As you can see our function failed because the sorting does work for primitives but simply not for objects.

Let's remove sorting and try different method.

function inArray(array, el) {
  for (var i = array.length; i--; ) {
    if ( array[i] === el ) return true;
  }
  return false;
}

function arraysEqual(a, b) {
  if (a === b) return true;
  if (a == null || b == null) return false;
  if (a.length !== b.length) return false;

  for (var i = a.length; i--; ) {
    if (!inArray(b, a[i])) {
      return false;
    }
  }
  return true;
}
Enter fullscreen mode Exit fullscreen mode

Let's test again.

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

arraysEqual(array1, array2); // true
arraysEqual(['1', 1], [1, '1']); // true
arraysEqual([1, 1, 2, 2], [1, 2, 1, 2]); // true

const a = [{a: 1}, {b: 2}];
const b = [{b: 2}, {a: 1}];

arraysEqual(a, b); // false
Enter fullscreen mode Exit fullscreen mode

As you can see the modified function works for primitives but not for objects, let's fix it in next level.

Level 4

The problem with the inArray function is that we are comparing two values using === operator, we need to find a way to compare objects also with deep nested.

We can use the popular lodash library's isEqual function here. Let's change it.

function inArray(array, el) {
  for (var i = array.length; i--; ) {
    if (_.isEqual(array[i], el)) return true;
  }
  return false;
}
Enter fullscreen mode Exit fullscreen mode

Now let's try it.

let a = [{a: 1}, {b: 2}];
let b = [{b: 2}, {a: 1}];

arraysEqual(a, b); // true

a = [{a: {b: {c: ['a', 'b', 'c']}}}, [1, 2, 3]];
b = [[1, 2, 3], {a: {b: {c: ['a', 'b', 'c']}}}];
arraysEqual(a, b); // true
Enter fullscreen mode Exit fullscreen mode

Level 5

Now what? If our code is working fine for all cases, then why do we need another level here?

There are some problems in the _.isEqual function.

https://github.com/lodash/lodash/issues/5401

https://github.com/lodash/lodash/issues/3640

https://github.com/lodash/lodash/issues/3428

Let's verify it now.

const a = [{m: new Map([['x', 'y']])}]
const b = [{m: new Map([['y', 'x']])}]

arraysEqual(a, b); // true

console.log(a, b);
/* 
[ { m: Map(1) { 'x' => 'y' } } ] 
[ { m: Map(1) { 'y' => 'x' } } ]
*/
Enter fullscreen mode Exit fullscreen mode

So, what is next?

Let me introduce our organization's Standard Library function isEqlArr here.

import { isEqlArr, shuffle } from '@opentf/std';

let a = [1, 2, 3];
let b = [2, 3, 1];
isEqlArr(a, b); // true

a = ['1', 1];
b = [1, '1'];
isEqlArr(a, b); // true

a = [1, 1, 2, 2];
b = [1, 2, 1, 2];
isEqlArr(a, b); // true

a = 'Apple'.split('')
b = shuffle('Apple')
isEqlArr(a, b); // true

a = [{a: 1}, {b: 2}];
b = [{b: 2}, {a: 1}];
isEqlArr(a, b); // true

a = [{a: {b: {c: ['a', 'b', 'c']}}}, [1, 2, 3]];
b = [[1, 2, 3], {a: {b: {c: ['a', 'b', 'c']}}}];
isEqlArr(a, b); // true

a = [{m: new Map([['x', 'y']])}]
b = [{m: new Map([['y', 'x']])}]
isEqlArr(a, b); // false
Enter fullscreen mode Exit fullscreen mode

Example: Real-World Use Case

In this example, we are going to find out how many sets of the same products were in the invoices of a company.

Note: If it doesn't make sense, just ignore this example.

import { isEqlArr } from '@opentf/std';

const invoices = [
  {
    userID: 'c2607dc9-bd74-446d-ac83-de201d158b87',
    products: [
      {
        id: '4e5fe7a0-0fd2-44f3-bdcd-6a3b2eea4b50',
        name: 'Product A'
      },
       {
        id: '863cb499-1943-473b-85f2-285ed9128c74',
        name: 'Product B'
      }
    ]
  },
  {
    userID: '6e523056-1601-41ba-b89e-6ae41164a843',
    products: [
      {
        id: '4e5fe7a0-0fd2-44f3-bdcd-6a3b2eea4b50',
        name: 'Product A'
      },
       {
        id: '401cc691-92f1-4e9e-bd78-995cb9663c5e',
        name: 'Product C'
      },

    ]
  },
  {
    userID: '3d375953-4c60-433c-b8c2-c45628d8f2d6',
    products: [
      {
        id: '863cb499-1943-473b-85f2-285ed9128c74',
        name: 'Product B'
      },
      {
        id: '4e5fe7a0-0fd2-44f3-bdcd-6a3b2eea4b50',
        name: 'Product A'
      }
    ]
  }
]

let count = 0

for (let i = 0; i < invoices.length; i += 1) {
  for (let j = 0; j < invoices.length; j += 1) {
    if (i !== j) {
      if (isEqlArr(invoices[i].products, invoices[j].products)) {
        count++
      }
    }
  }
}

console.log(count); // 2
Enter fullscreen mode Exit fullscreen mode

Note: You can try out these examples on our online Node.js REPL

Conclusion

Here, we have seen various levels in comparing arrays elements unordered.

The new Standard library function is used to achieve the same for the following reasons:

  • Cross-Environment Compatibility: Execute seamlessly in browsers, Node.js, Bun, Deno, etc.

  • TypeScript Support

  • Works with both CJS & ESM

  • Supports some Older Browsers & Node.js >= 16

If you need to find out the performance of the lib, please check out these benchmarks.

Please don't forget to check out our important Articles:

Happy coding! 🚀

🙏 Thanks for reading.

References:

https://stackoverflow.com/questions/29672847/how-to-compare-contents-of-javascript-array-but-not-the-order-of-them

https://stackoverflow.com/questions/3243275/javascript-arrays-checking-two-arrays-of-objects-for-same-contents-ignoring-o

https://stackoverflow.com/questions/47072055/how-to-make-a-deep-comparison-of-unordered-arrays?noredirect=1&lq=1

https://stackoverflow.com/questions/7837456/how-to-compare-arrays-in-javascript

Top comments (0)