I0_ol
I0_ol

Reputation: 1109

Writing a correct assertion test for equal arrays

I'm having trouble writing a correct assertion test. It works fine on single arrays but it doesn't work when testing an array of arrays. The problem seems to be with areEqualItems but I don't know why. What am I doing wrong?

function assertArraysEqual(actual, expected, testName){
    var areEqualLength = actual.length === expected.length;
    var areEqualItems = actual.every(function(item, i){
        return item === expected[i]
    });
    if(areEqualLength && areEqualItems){
        console.log('passed');
    } else {
        console.log('FALIED [' + testName + '] Expected ' + expected + ', but got ' + actual);
    }
}

var arr1 = [0,1,2,3];
var arr2 = [0,1,2,3];

var arr3 = [ [0,1,2,3], [0,1,2,3] ];
var arr4 = [ [0,1,2,3], [0,1,2,3] ];

assertArraysEqual(arr1, arr2, 'arrays should be equal'); // passed
assertArraysEqual(arr3, arr4, 'arrays should be equal'); // FALIED [arrays should be equal] Expected 0,1,2,3,0,1,2,3, but got 0,1,2,3,0,1,2,3

Upvotes: 2

Views: 1344

Answers (6)

Mark
Mark

Reputation: 92460

You can make a very succinct equals function. Just check the edge cases where the lengths aren't equal or they aren't arrays and then recurse:

var arr3 = [[0,1,2,3], [0,1,2,3]];
var arr4 = [[0,1,2,3], [0,1,2,3]];


function equal(a, b){
  if (!Array.isArray(a) || !Array.isArray(b)) return a === b
  if (a.length !== b.length) return false

  return a.every((item, i) => equal(item, b[i]))
}

console.log(equal(arr3, arr4))
console.log(equal([[1, 2], [3, 4]], [[2, 2], [3, 4]]))

Upvotes: 3

Nadav
Nadav

Reputation: 1145

Comparing the arrays (and their child arrays, and their child arrays) can become very expensive quite quickly. In addition the code you wrote, as well as the accepted answer, don't take into account associative arrays (aka objects). I would suggest converting both of the arrays into JSON strings and then compare them as strings.

function assertArraysEqual(actual, expected, testName){
    if ( actual.length === expected.length && JSON.stringify(actual) == JSON.stringify(expected) ) console.log('passed');
    else console.log('FALIED [' + testName + '] Expected ' + expected + ', but got ' + actual);
}

var arr1 = [0,1,2,3];
var arr2 = [0,1,2,3];

var arr3 = [ [0,1,2,3], [0,1,2,3] ];
var arr4 = [ [0,1,2,3], [0,1,2,3] ];

assertArraysEqual(arr1, arr2, 'arrays should be equal'); 
assertArraysEqual(arr3, arr4, 'arrays should be equal');

Upvotes: 0

gr4viton
gr4viton

Reputation: 1514

An other solution to let this job to a third party, which can be for example lodash.

Lodash has isEqual method which can do the job for you.

Check out the documentation here.

Sample code:

function assertArraysEqual(actual, expected, testName) {
    if (_.isEqual(actual, expected)) {
        console.log('passed');
    } else {
        console.log(`FALIED [${testName}] Expected ${expected}, but got ${actual}`);
    }
}

Upvotes: 0

Shubham Gupta
Shubham Gupta

Reputation: 2646

Why don't you stringify the arrays and then compare? It will reduce your code significantly take a look.

function assertArraysEqual(actual, expected) {
    const actualStr = JSON.stringify(actual);
    const expectedStr = JSON.stringify(expected);
    return actualStr === expectedStr;
}

Upvotes: 1

Barmar
Barmar

Reputation: 781731

You need to make it recurse if the element is another array.

Also, there's no point in checking if the items are equal when the lengths don't match.

It would also be better if you took the logging out of the comparison function. I had to make it conditional on whether the testName argument was supplied, so that it wouldn't log during all the recursive calls.

function assertArraysEqual(actual, expected, testName) {
  var areEqualLength = actual.length === expected.length;
  var areEqualItems = areEqualLength && actual.every(function(item, i) {

    if (Array.isArray(item) && Array.isArray(expected[i])) {
      return assertArraysEqual(item, expected[i]);
    } else {
      return item === expected[i]
    }
  });
  if (testName) {
    if (areEqualItems) {
      console.log('passed');
    } else {
      console.log('FAILED [' + testName + '] Expected ' + expected + ', but got ' + actual);
    }
  }
  return areEqualItems;
}

var arr1 = [0, 1, 2, 3];
var arr2 = [0, 1, 2, 3];

var arr3 = [
  [0, 1, 2, 3],
  [0, 1, 2, 3]
];
var arr4 = [
  [0, 1, 2, 3],
  [0, 1, 2, 3]
];
var arr5 = [
  [0, 1, 2, 3],
  [0, 2, 3, 4]
];

assertArraysEqual(arr1, arr2, 'arrays should be equal'); // passed
assertArraysEqual(arr3, arr4, 'arrays should be equal'); // FALIED [arrays should be equal] Expected 0,1,2,3,0,1,2,3, but got 0,1,2,3,0,1,2,3
assertArraysEqual(arr3, arr5, 'arrays should not be equal');

Upvotes: 2

Anand Undavia
Anand Undavia

Reputation: 3543

Here is a little help on how to make it recursive:

function areArraySame(one, two) {
    var length = one.length === two.length;
    var allElems = one.every(function(item, i) {
        if (item instanceof Array) {
            return areArraySame(item, two[i]); // <-- recurse!
        }
        return item === two[i];
    });

    return length && allElems;
}

Upvotes: 1

Related Questions