Javascript Arrays - Checking two arrays of objects for same contents, ignoring order
Asked Answered
D

5

18

I have two JavaScript arrays (A and B) that contain objects that I created. I want to check that all the objects in array A are contained in array B, but not necessarily in the same order.

What is the best way to do this?

Edit:

They are all actual objects, not primitives, so I will need to compare their contents and structure as well (maybe using something like JSON.stringify).

I want to do this because I'm learning Test-Driven Development, and I want to test functions that return lists of objects. I need to test whether the returned lists have the expected objects in them or not (order doesn't matter in this case).

Dike answered 14/7, 2010 at 3:48 Comment(7)
Are they primitives such as true, false, 23 or actual objects? Also can you explain why you want to do this? There may be a better way.Hedonics
Edited question for clarification.Dike
Why not first sort and then use JSON.stringify to compareBorsch
Actually you can compare objects by reference.Hedonics
How complex are these objects you're comparing? Do they have characteristics you're going to need to check as well (methods, etc)?Temikatemp
@Temikatemp - That's a good point. If that is the case a deep compare may be the only way.Hedonics
@Hedonics - It's good practice for recursive processing that's for sure. It can take some time though.Temikatemp
S
3

Usage: isEqArrays(arr1, arr2)

//
// Array comparsion
//

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

function isEqArrays(arr1, arr2) {
  if ( arr1.length !== arr2.length ) {
    return false;
  }
  for ( var i = arr1.length; i--; ) {
    if ( !inArray( arr2, arr1[i] ) ) {
      return false;
    }
  }
  return true;
}
Stomatic answered 14/7, 2010 at 9:47 Comment(3)
Is this faster than @ChaosPandion's solution?Dike
it's cross browser. Chaos's isn't. He uses language features which are not well-supported yet.Stomatic
This code doesn't work: isEqArrays([0, 0, 1], [0, 1, 1]) returns true.Fertilizer
C
4

With ES6 you could use every and some (and length).

let A = [1, 2, 3];
let B = [2, 3, 1];

// all objects in A are contained in B (A ⊆ B)
// you can compare a <-> b however you'd like (here just `a === b`)
let AsubB = A.every(a => B.some(b => a === b));

// A and B are the same length
let sameLength = A.length === B.length;

// their contents are as equal as previously tested:
let equal = AsubB && sameLength;
Connaught answered 7/6, 2018 at 15:23 Comment(2)
Checking for containment, even when you use both directions, does not account for multiple equal elements; e.g., consider [1,2,3,3] and [1,2,2,3].Comenius
for checking two object properties it's fine; moreover in the question is not requested to check for repeats. I used it with an object containing dates from and to and works nice.Gasket
S
3

Usage: isEqArrays(arr1, arr2)

//
// Array comparsion
//

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

function isEqArrays(arr1, arr2) {
  if ( arr1.length !== arr2.length ) {
    return false;
  }
  for ( var i = arr1.length; i--; ) {
    if ( !inArray( arr2, arr1[i] ) ) {
      return false;
    }
  }
  return true;
}
Stomatic answered 14/7, 2010 at 9:47 Comment(3)
Is this faster than @ChaosPandion's solution?Dike
it's cross browser. Chaos's isn't. He uses language features which are not well-supported yet.Stomatic
This code doesn't work: isEqArrays([0, 0, 1], [0, 1, 1]) returns true.Fertilizer
H
2

This is probably the simplest method if not the slowest.

var o = { PropA: 1, PropB: 2 };
var a = [1, 2, 3, 4, o];
var b = [2, 3, 4, 1];

var c = a.filter(function(value, index, obj) {
    return b.indexOf(value) > -1;
});

if (c.length !== a.length) {
    throw new Error("Array b is missing some elements!");
}

indexOf will only check that they refer to the same object. If you want to check value equivalence you will have to do a deep compare of the properties or use JSON.stringify as you mention in your question.

Hedonics answered 14/7, 2010 at 4:6 Comment(2)
Does .indexOf check for object equivalency too? As in, if I make an o2 = { PropA: 2, PropB: 2} and put it in b, would it return something other than -1?Dike
As with the other answer, this returns a false positive for [0, 1, 1] and [0, 0, 1].Fertilizer
A
0

Simple & Elegant

function isEqual(arr1, arr2) {
    if (arr1.length !== arr2.length)
        return false;
    return arr1.every(x => arr2.includes(x));
}

If duplication is important, use this

function isEqual(arr1, arr2) {
    if (arr1.length !== arr2.length)
        return false;
    arr1.sort();
    arr2.sort();
    for (var i = 0; i < arr1.length; i++) {
        if (arr1[i] !== arr2[i])
            return false;
    }
    return true;
}
Adrenal answered 25/1, 2022 at 15:17 Comment(1)
isEqual([0, 1, 1], [0, 0, 1]) returns true...Zapata
S
0

Hi it seem more difficult than I thought and I need to code it myself. Please check my solution:

//usage
const arr1 = [{
    a: 1,
    b: 2
  },
  {
    a: 2,
    b: 1
  },
  {
    a: 1,
    b: 2,
    c: 3
  },
];
const arr2 = [{
    a: 1,
    b: 2,
    c: 3
  },
  {
    a: 1,
    b: 2
  },
  {
    a: 2,
    b: 1
  },
];
alert(compareTwoArray(arr1, arr2));

function compareTwoArray(arr1, arr2) {
  if (arr1.length != arr2.length || arr1.length == arr2.length == 0) {
    return false;
  }
  //clone arr to process
  let arr01 = JSON.parse(JSON.stringify(arr1));
  let arr02 = JSON.parse(JSON.stringify(arr2));
  for (let i = (arr01.length - 1); i >= 0; i--) {
    for (let j = (arr02.length - 1); j >= 0; j--) {
      if (JSON.stringify(arr01[i]) === JSON.stringify(arr02[j])) {
        arr01.splice(i, 1);
        arr02.splice(j, 1);
      }
    }
  }
  if (arr01.length == 0 && arr02.length == 0) {
    return true;
  } else {
    return false;
  }
}

This is for array of values only:

//usage
alert(compareTwoArray([0, 0, 1, 1], [1, 0, 1, 0]));

function compareTwoArray(arr1, arr2) {
  if (arr1.length != arr2.length || arr1.length == arr2.length == 0) {
    return false;
  }
  //clone arr to process
  let arr01 = JSON.parse(JSON.stringify(arr1));
  let arr02 = JSON.parse(JSON.stringify(arr2));
  for (let i = (arr01.length - 1); i >= 0; i--) {
    for (let j = (arr01.length - 1); j >= 0; j--) {
      if (arr01[i] == arr02[j]) {
        //remove duplicate item from both array
        arr01.splice(i, 1);
        arr02.splice(j, 1);
      }
    }
  }
  if (arr01.length == 0 && arr02.length == 0) {
    return true;
  } else {
    return false;
  }
}
Selfwill answered 20/2 at 7:17 Comment(5)
is it working with actual objects, not primitives?Polyclitus
@Polyclitus I think you need to add code to compare two object instead of if (arr01[i] == arr02[j]) and then it can be work on arrays of objectsSelfwill
The question mentioned object comparison so your solution does not answer the question. You have to consider nested objects as well (ie. a tree of objects).Polyclitus
@Polyclitus oh right! I just updated my answer for arrays of objectsSelfwill
Good, your approach is generating a lot of new objects and strings but is doing the job. I would have opted for a recursive in-place comparison.Polyclitus

© 2022 - 2024 — McMap. All rights reserved.