4

I have to compare two unsorted arrays of objects, for example the following code should return true:

compareObjs(
    [{ foo: 'foo', bar: 'bar' }, { baz: 'baz' }], 
    [{ baz: 'baz' }, { foo: 'foo', bar: 'bar' }]
)

I know there are already plenty of answers about comparing arrays of objects, but I did not really find the clear one for comparing the unsorted version of arrays.

Shota
  • 6,910
  • 9
  • 37
  • 67
  • 2
    `function compareObjs() { return true }` will do that. *Why* do you expect it to return true, and when should it not? – Bergi Dec 01 '17 at 08:23
  • 1
    I expect it to be true because the length of both arrays are the same and they contain the same objects (same keys and values). Object references do not matter in this case. – Shota Dec 01 '17 at 08:24
  • Several questions on here asks the same. Check out: https://stackoverflow.com/questions/9191791/compare-arrays-of-objects-optimal-way and https://stackoverflow.com/questions/201183/how-to-determine-equality-for-two-javascript-objects/3198202#3198202 – xpqz Dec 01 '17 at 08:26
  • 1
    @Shota Could you just sort them? – Bergi Dec 01 '17 at 08:28
  • I've read that answers, but JSON.stringify won't work, as the arrays are not sorted and I cant use lodash, I have to do this in Vanilla JS – Shota Dec 01 '17 at 08:28
  • @Bergi yes one solution that I see is that. Firstly stringify the containing objects and then sort them, and finally, join and compare. I will wait if someone proposes any better way. – Shota Dec 01 '17 at 08:32
  • This may helps [How to get the difference between two arrays in Javascript?](https://stackoverflow.com/q/1187518/6521116) – LF00 Dec 01 '17 at 08:57

6 Answers6

2

Here another possibility:

const array2 = [1,3,2,4,5];

const isInArray1 = array1.every(item => array2.find(item2 => item===item2))
const isInArray2 = array2.every(item => array1.find(item2 => item===item2))

const isSameArray = array1.length === array2.length && isInArray1 && isInArray2

console.log(isSameArray); //true
Jöcker
  • 5,281
  • 2
  • 38
  • 44
  • This doesn't work for objects, and could be improved for primitive types by using `.includes()` in place of the `.find()` – GarethAS Nov 05 '21 at 12:34
0

You could convert each object to string using JSON.stringify(). Then sort these arrays. And compare each pair.

If you dont't mind about es6:

let arr1 = [{ foo: 'foo', bar: 'bar' }, { baz: 'baz' }];
let arr2 = [{ baz: 'baz' }, { foo: 'foo', bar: 'bar' }];

const Compare = (arr1, arr2) => {
  if (arr1.length != arr2.length) {
    return false
  }

  let a1 = arr1.map(e => JSON.stringify(e)).sort()
  let a2 = arr2.map(e => JSON.stringify(e)).sort()
  
  return !a1.map((e, i) => e == a2[i]).includes(false)
}

console.log(Compare(arr1, arr2))
Mikhail Katrin
  • 2,304
  • 1
  • 9
  • 17
0

function givemeSanitizedObject(arr1) {
  let obj = {}
  arr1.forEach(function(element, index, array) {
      obj = {
        ...obj,
        ...element,
      }
  })
  return obj
 }

 function compareObjs(arr1, arr2) {
    let obj1 = givemeSanitizedObject(arr1)
    let obj2 = givemeSanitizedObject(arr2)

    for (var property in obj1) {
      if (Object.hasOwnProperty(property)) {
        if (obj1.property !== obj2.property) {
          return false
        }
      }
    }
    return true
}

console.log(compareObjs(
  [{
    foo: 'foo',
    bar: 'bar'
  }, {
    baz: 'baz'
  }], [{
    baz: 'baz'
  }, {
    foo: 'foo',
    bar: 'bar'
  }]
))
simbathesailor
  • 3,681
  • 2
  • 19
  • 30
  • Unfortunately, this doesn't work: you're not keeping the structure of each object. Have a look here, it's your code with two clearly different arrays: https://jsfiddle.net/1myf4cvk/ – Gerardo Furtado Dec 01 '17 at 08:44
  • Yes i think he has a different use case . i saw it now. – simbathesailor Dec 01 '17 at 08:47
  • You'll want to use `reduce` instead of `forEach` in `giveMeSanitisedObject`. And you need to use `[property]` instead of `.property`. – Bergi Dec 01 '17 at 08:50
0

A basic approach will be to loop through all the objects of one of the arrays and see if you can find similar objects in the other array.

Here is an example:

function compareArrays(arr1, arr2){
    if(arr1.length != arr2.length){
        return false;
    }

    for(var i = 0; i < arr1.length; i++){
        var value1 = arr1[i];
        var keys1 = Object.keys(value1);
        var found = false;
        for(var j = 0; j < arr2.length; j++){
           var value2 = arr2[j];
           var keys2 = Object.keys(value2);
           if(keys1.length == keys2.length 
              && keys1.every(k => keys2.includes(k)) 
              && keys1.every(k => value1[k] == value2[k])){
              found = true;
              break;
           }
        }
        if(!found){
           return false;
        }
    }
   
    return true;
}

var comp = compareArrays(
    [{ foo: 'foo', bar: 'bar' }, { baz: 'baz'}], 
    [{ baz: 'baz' }, { foo: 'foo', bar: 'bar'}]
);

console.log(comp);
Titus
  • 22,031
  • 1
  • 23
  • 33
0

You could use a hash table and check if the proprties and values are the same. It works with the given types.

function compareArrays(array1, array2) {
    var hash = {};

    if (array1.length !== array2.length) {
        return false;
    }

    array1.forEach(function (o) {
        var keys = Object.keys(o).sort(),
            key = keys.join('|'),
            value = keys.map(function (k) { return o[k]; }).join('|');

        hash[key] = hash[key] || {};
        hash[key][value] = (hash[key][value] || 0) + 1;
    });

    return array2.every(function (o) {
        var keys = Object.keys(o).sort(),
            key = keys.join('|'),
            value = keys.map(function (k) { return o[k]; }).join('|');

        return hash[key] && hash[key][value] && hash[key][value]--;
    });
}

console.log(compareArrays([{ foo: 'foo', bar: 'bar' }, { baz: 'baz' }], [{ baz: 'baz' }, { foo: 'foo', bar: 'bar' }]));
console.log(compareArrays([{ foo: 'foo', bar: 'bar' }, { baz: 'baz' }, { baz: 'baz' }], [{ baz: 'baz' }, { foo: 'foo', bar: 'bar' }]));
console.log(compareArrays([{ foo: 'foo', bar: 'bar' }, { baz: 'baz' }, { foo: 'baz' }], [{ baz: 'baz' }, { foo: 'foo', bar: 'bar' }]));
console.log(compareArrays([{ foo: 'foo', bar: 'bar' }, { baz: 'baz' }, { foo: 'baz' }], [{ baz: 'baz' }, { foo: 'foo', bar: 'bar' }, { foo: 42 }]));
Nina Scholz
  • 376,160
  • 25
  • 347
  • 392
  • It doesn't work with objects whose keys or values contain a `|` though. Or with values that are not strings at all. I'd recommend to use `JSON.stringify()` on the sorted arrays of keys and values instead. – Bergi Dec 01 '17 at 08:52
  • Also you could simplify by concatenating `key` and `value` instead of nesting the lookup table. – Bergi Dec 01 '17 at 08:53
0

You can try something like below

  • Reduce multiple objects in each array into one
  • Sort keys and build a 2D array or build a new object
  • Compare the two newly built data structures (2D arrays / objects)

This can be extrapolted to any number of arrays

const arr1 = [{ foo: 'foo', bar: 'bar' }, { baz: 'baz' }]; 
const arr2 = [{ baz: 'baz' }, { foo: 'foo', bar: 'bar' }];

// Method that sorts the object keys and builds a new object
// sortObj = (obj) =>
//   Object.keys(obj).sort().reduce((a, b) => {a[b] = obj[b]; return a}, {})
  
// Method that sorts keys and builds a 2D array
sortObj = (obj) =>
    Object.keys(obj).sort().map((key) => [key, obj[key]])

compareObj = (arr1, arr2) => {
  if(arr1.length !== arr2.length)
     return false;
 
  // 1st array reduce
  const reduceArr1 = arr1.reduce((a, b) => ({...a, ...b}), {});
  
  // 2nd array reduce
  const reduceArr2 = arr2.reduce((a, b) => ({...a, ...b}), {});
  
  // Comparing the sortedObjects
  return JSON.stringify(sortObj(reduceArr1)) === JSON.stringify(sortObj(reduceArr2))
}

console.log(compareObj(arr1, arr2))
Nandu Kalidindi
  • 6,075
  • 1
  • 23
  • 36