Compare arrays of objects independent of the order
Asked Answered
C

3

5

I have 2 arrays of objects and I have to compare them, but the order of the objects DOES NOT matter. I can't sort them because I won't have their keys' names because the functions must be generic. The only information that I'll have about the array is that both array's objects have the same amount of keys and those keys have the same name. So the array1 must contain the same objects as the array2.

var array1 = [{"key1":"Banana", "key2":"Yammy"}, {"key1":"Broccoli", "key2":"Ew"}];
var array2 = [{"key1":"Broccoli", "key2":"Ew"}, {"key1":"Banana", "key2":"Yammy"}];

In the example, array1 must be equal array2. I tryed to use the chai .eql() method but it didn't work.

Cooperative answered 31/10, 2017 at 17:34 Comment(2)
What does "compare them" mean? Do you just need to know if they are the same or not? A boolean response?Childish
Are all values string type ?Sodamide
R
1

You can array#join each value of the object on an separator and then generate a new array of string and then compare each values using array#every and array#includes

var array1 = [{"key1":"Banana", "key2":"Yammy"}, {"key1":"Broccoli", "key2":"Ew"}];
    array2 = [{"key1":"Broccoli", "key2":"Ew"}, {"key1":"Banana", "key2":"Yammy"}];
    values = (o) => Object.keys(o).sort().map(k => o[k]).join('|'),
    mapped1 = array1.map(o => values(o)),
    mapped2 = array2.map(o => values(o));

var res = mapped1.every(v => mapped2.includes(v));

console.log(res);
Raffaello answered 31/10, 2017 at 17:51 Comment(6)
This may fail when one array has more entries than the other, when a key has the pipe symbol, when objects have different keys, ...Nudity
It worked like a charm. There is an easy way to print an array of objects that doesn't match? For exemple, if instead of "Banana" one of the arrays have "Bananas".Cooperative
@trincot, I'm verifying the length of the arrays before comparing the objects.Cooperative
Examples of wrong results: change first "key1" to "key0": result is still true. Or, remove "key2": "Yammy" and change "Banana" to "Banana|Yammy": result is still true.Nudity
@Nudity You are correct on both account, but the question mentioned that The only information that I'll have about the array is that both array's objects have the same amount of keys and those keys have the same name.Raffaello
Still then you can have values Banana|Yammy and Nice compared with Banana and Yammy|Nice and have equality. Also running time is O(n²).Nudity
N
7

The following solution:

  • will verify that the arrays have an equal number of elements
  • does not impose restrictions on keys (as to not contain a certain delimiter)
  • requires both keys and (string) values to be the same
  • has a time complexity of O(nlogn) (instead of O(n²) as some other solutions here)

function equalArrays(a, b) {
    if (a.length !== b.length) return false;
    const ser = o => JSON.stringify(Object.keys(o).sort().map( k => [k, o[k]] ));
    a = new Set(a.map(ser));
    return b.every( o => a.has(ser(o)) );
}

// Example
var array1 = [{"key1":"Banana", "key2":"Yammy"}, {"key1":"Broccoli", "key2":"Ew"}];
var array2 = [{"key1":"Broccoli", "key2":"Ew"}, {"key1":"Banana", "key2":"Yammy"}];
console.log(equalArrays(array1, array2)); // true
// Example with different key name
var array1 = [{"key0":"Banana", "key2":"Yammy"}, {"key1":"Broccoli", "key2":"Ew"}];
var array2 = [{"key1":"Broccoli", "key2":"Ew"}, {"key1":"Banana", "key2":"Yammy"}];
console.log(equalArrays(array1, array2)); // false
Nudity answered 31/10, 2017 at 18:41 Comment(0)
R
1

You can array#join each value of the object on an separator and then generate a new array of string and then compare each values using array#every and array#includes

var array1 = [{"key1":"Banana", "key2":"Yammy"}, {"key1":"Broccoli", "key2":"Ew"}];
    array2 = [{"key1":"Broccoli", "key2":"Ew"}, {"key1":"Banana", "key2":"Yammy"}];
    values = (o) => Object.keys(o).sort().map(k => o[k]).join('|'),
    mapped1 = array1.map(o => values(o)),
    mapped2 = array2.map(o => values(o));

var res = mapped1.every(v => mapped2.includes(v));

console.log(res);
Raffaello answered 31/10, 2017 at 17:51 Comment(6)
This may fail when one array has more entries than the other, when a key has the pipe symbol, when objects have different keys, ...Nudity
It worked like a charm. There is an easy way to print an array of objects that doesn't match? For exemple, if instead of "Banana" one of the arrays have "Bananas".Cooperative
@trincot, I'm verifying the length of the arrays before comparing the objects.Cooperative
Examples of wrong results: change first "key1" to "key0": result is still true. Or, remove "key2": "Yammy" and change "Banana" to "Banana|Yammy": result is still true.Nudity
@Nudity You are correct on both account, but the question mentioned that The only information that I'll have about the array is that both array's objects have the same amount of keys and those keys have the same name.Raffaello
Still then you can have values Banana|Yammy and Nice compared with Banana and Yammy|Nice and have equality. Also running time is O(n²).Nudity
S
0

You can do something like following:

For each object in each array you can calc its representation:

arr1.forEach( (obj) => {
    obj.representation = '';
    for (let key of Object.keys(obj)) {
      obj.representation += obj[key];
    }
}

Same for arr2

now you can sort both arrays by representation for example and then compare.

To sort do the following:

arr1.sort( (a,b) => { return a.representation > b.representation } );
arr2.sort( (a,b) => { return a.representation > b.representation } );

After sorting you can compare both arrays

let equal = arr1.every( (el, i) => arr2[i]===el );
Sodamide answered 31/10, 2017 at 17:47 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.