Expect Arrays to be equal ignoring order

JavascriptJasmineKarma Jasmine

Javascript Problem Overview


With Jasmine is there a way to test if 2 arrays contain the same elements, but are not necessarily in the same order? ie

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

expect(array1).toEqualIgnoreOrder(array2);//should be true

Javascript Solutions


Solution 1 - Javascript

If it's just integers or other primitive values, you can sort() them before comparing.

expect(array1.sort()).toEqual(array2.sort());

If its objects, combine it with the map() function to extract an identifier that will be compared

array1 = [{id:1}, {id:2}, {id:3}];
array2 = [{id:3}, {id:2}, {id:1}];

expect(array1.map(a => a.id).sort()).toEqual(array2.map(a => a.id).sort());

Solution 2 - Javascript

You could use expect.arrayContaining(array) from standard jest:

  const expected = ['Alice', 'Bob'];
  it('matches even if received contains additional elements', () => {
    expect(['Alice', 'Bob', 'Eve']).toEqual(expect.arrayContaining(expected));
  });

Solution 3 - Javascript

jasmine version 2.8 and later has

jasmine.arrayWithExactContents()

Which expects that an array contains exactly the elements listed, in any order.

array1 = [1,2,3];
array2 = [3,2,1];
expect(array1).toEqual(jasmine.arrayWithExactContents(array2))

See https://jasmine.github.io/api/3.4/jasmine.html

Solution 4 - Javascript

simple...

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

expect(array1).toEqual(jasmine.arrayContaining(array2));

Solution 5 - Javascript

The jest-extended package provides us few assertions to simplify our tests, it's less verbose and for failing tests the error is more explicit.

For this case we could use toIncludeSameMembers

expect([{foo: "bar"}, {baz: "qux"}]).toIncludeSameMembers([{baz: "qux"}, {foo: "bar"}]);

Solution 6 - Javascript

// check if every element of array2 is element of array1
// to ensure [1, 1] !== [1, 2]
array2.forEach(x => expect(array1).toContain(x))

// check if every element of array1 is element of array2
// to ensure [1, 2] !== [1, 1]
array1.forEach(x => expect(array2).toContain(x))

// check if they have equal length to ensure [1] !== [1, 1]
expect(array1.length).toBe(array2.length)

Solution 7 - Javascript

//Compare arrays without order
//Example
//a1 = [1, 2, 3, 4, 5]
//a2 = [3, 2, 1, 5, 4]
//isEqual(a1, a2) -> true
//a1 = [1, 2, 3, 4, 5];
//a2 = [3, 2, 1, 5, 4, 6];
//isEqual(a1, a2) -> false


function isInArray(a, e) {
  for ( var i = a.length; i--; ) {
    if ( a[i] === e ) return true;
  }
  return false;
}

function isEqArrays(a1, a2) {
  if ( a1.length !== a2.length ) {
    return false;
  }
  for ( var i = a1.length; i--; ) {
    if ( !isInArray( a2, a1[i] ) ) {
      return false;
    }
  }
  return true;
}

Solution 8 - Javascript

There is currenly a matcher for this USE CASE:

https://github.com/jest-community/jest-extended/pull/122/files

test('passes when arrays match in a different order', () => {
  expect([1, 2, 3]).toMatchArray([3, 1, 2]);
  expect([{ foo: 'bar' }, { baz: 'qux' }]).toMatchArray([{ baz: 'qux' }, { foo: 'bar' }]);
});

Solution 9 - Javascript

This approach has worse theoretical worst-case run-time performance, but, because it does not perform any writes on the array, it might be faster in many circumstances (haven't tested performance yet):

WARNING: As Torben pointed out in the comments, this approach only works if both arrays have unique (non-repeating) elements (just like several of the other answers here).

/**
 * Determine whether two arrays contain exactly the same elements, independent of order.
 * @see https://stackoverflow.com/questions/32103252/expect-arrays-to-be-equal-ignoring-order/48973444#48973444
 */
function cmpIgnoreOrder(a, b) {
  const { every, includes } = _;
  return a.length === b.length && every(a, v => includes(b, v));
}

// the following should be all true!
const results = [  !!cmpIgnoreOrder([1,2,3], [3,1,2]),
  !!cmpIgnoreOrder([4,1,2,3], [3,4,1,2]),
  !!cmpIgnoreOrder([], []),
  !cmpIgnoreOrder([1,2,3], [3,4,1,2]),
  !cmpIgnoreOrder([1], []),
  !cmpIgnoreOrder([1, 3, 4], [3,4,5])
];

console.log('Results: ', results)
console.assert(_.reduce(results, (a, b) => a && b, true), 'Test did not pass!');

<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.5/lodash.js"></script>

Solution 10 - Javascript

function equal(arr1, arr2){
    return arr1.length === arr2.length
    &&
    arr1.every((item)=>{
        return arr2.indexOf(item) >-1
    }) 
    &&
    arr2.every((item)=>{
        return arr1.indexOf(item) >-1
    })
}

The idea here is to first determine if the length of the two arrays are same, then check if all elements are in the other's array.

Solution 11 - Javascript

Here's a solution that will work for any number or arrays

https://gist.github.com/tvler/cc5b2a3f01543e1658b25ca567c078e4

const areUnsortedArraysEqual = (...arrs) =>
  arrs.every((arr, i, [first]) => !i || arr.length === first.length) &&
  arrs
    .map(arr =>
      arr.reduce(
        (map, item) => map.set(item, (map.get(item) || 0) + 1),
        new Map(),
      ),
    )
    .every(
      (map, i, [first]) =>
        !i ||
        [...first, ...map].every(([item]) => first.get(item) === map.get(item)),
    );

Some tests (a few answers to this question don't account for arrays with multiple items of the same value, so [1, 2, 2] and [1, 2] would incorrectly return true)

[1, 2] true
[1, 2], [1, 2] true
[1, 2], [1, 2], [1, 2] true
[1, 2], [2, 1] true
[1, 1, 2], [1, 2, 1] true
[1, 2], [1, 2, 3] false
[1, 2, 3, 4], [1, 2, 3], [1, 2] false
[1, 2, 2], [1, 2] false
[1, 1, 2], [1, 2, 2] false
[1, 2, 3], [1, 2], [1, 2, 3] false

Solution 12 - Javascript

This algorithm is great for arrays where each item is unique. If not, you can add in something to check for duplicates...

tests = [  [ [1,0,1] , [0,1,1] ],
  [ [1,0,1] , [0,0,1] ], //breaks on this one...
  [ [2,3,3] , [2,2,3] ], //breaks on this one also...
  [ [1,2,3] , [2,1,3] ],
  [ [2,3,1] , [1,2,2] ],
  [ [2,2,1] , [1,3,2] ]
]

tests.forEach(function(test) {
  console.log('eqArraySets( '+test[0]+' , '+test[1]+' ) = '+eqArraySets( test[0] , test[1] ));
});


function eqArraySets(a, b) {
	if ( a.length !== b.length ) { return false; }
	for ( var i = a.length; i--; ) {
		if ( !(b.indexOf(a[i])>-1) ) { return false; }
		if ( !(a.indexOf(b[i])>-1) ) { return false; }
	}
	return true;
}

Solution 13 - Javascript

I am currently using this helper function (for TypeScript). It makes sure that arrays that have non unique elements are supported as well.

function expectArraysToBeEqualIgnoringOrder<T>(arr1: T[], arr2: T[]) {

    while(arr1.length > 0) {

        expect(arr1.length).toEqual(arr2.length)

        const elementToDelete = arr1[0]

        arr1 = arr1.filter(element => element !== elementToDelete)
        arr2 = arr2.filter(element => element !== elementToDelete)

    }

    expect(arr2.length).toEqual(0)

}

Many of the other asnwers do not correctly handle cases like this:

array1: [a, b, b, c]
array2: [a, b, c, c]

Here the number of elements in both arrays is the same and both arrays contain all elements from the other array, yet they are different arrays and the test should fail. It runs in O(n^2) (precisely (n^2 + n) / 2), so it's not suitable for very large arrays, but it's suitable for arrays that are not easilly sorted and therefore can not be compared in O(n * log(n))

Solution 14 - Javascript

Jest has a function called expect.arrayContaining which will do exactly what you want:

expect(array1).toEqual(expect.arrayContaining(array2))

you may want to check if they are with the same length too, since the test will pass if

> the expected array is a subset of the received array

according to the doc.

EDIT: Sorry that I didn't notice the jasmine tag, this is a way that work with Jest

Solution 15 - Javascript

You could use something like:

expect(array1).toEqual(jasmine.arrayContaining(array2));

Remember import jasmine. Or add it to your .eslintrc

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionDavid says Reinstate MonicaView Question on Stackoverflow
Solution 1 - JavascriptColoured PandaView Answer on Stackoverflow
Solution 2 - JavascriptMarinaView Answer on Stackoverflow
Solution 3 - JavascriptkeksmastaView Answer on Stackoverflow
Solution 4 - JavascriptProfiProgView Answer on Stackoverflow
Solution 5 - Javascriptdave008View Answer on Stackoverflow
Solution 6 - JavascriptJannic BeckView Answer on Stackoverflow
Solution 7 - JavascriptRaviView Answer on Stackoverflow
Solution 8 - JavascriptDaniel MaldonadoView Answer on Stackoverflow
Solution 9 - JavascriptDomiView Answer on Stackoverflow
Solution 10 - JavascriptLying_catView Answer on Stackoverflow
Solution 11 - JavascriptTylerView Answer on Stackoverflow
Solution 12 - JavascriptJoe DFView Answer on Stackoverflow
Solution 13 - JavascriptGiorgio AcquatiView Answer on Stackoverflow
Solution 14 - JavascriptDavid LeeView Answer on Stackoverflow
Solution 15 - JavascriptKViin CoyoyView Answer on Stackoverflow