1

I've built my own ''array equality'' function and when all values in an array are unique it works as expected:

Example: (working)

var
   a = [1, 2, 3, ["a", "b"]],
   b = [1, 2, 3, ["a", "b"]];

arrayEquals(a, b); /* returns: [true, true, true, [true, true]] */

When there are duplicate values, however, the final result is damaged heavily:

Example: (non-working)

In this example, 1 exists twice in the first array. The problem is that the second 1 will return the first 1 of the second array as its match, even though that 1 has been already matched right a step ago by the previous 1 of the first array.

var
   a = [1, 1, 2],
   b = [1, 2, 2];

arrayEquals(a, b); /* returns: [true, false, false] */
                   /* should return: [true, false, true] */

Question:

Is there a way to safely remove or avoid checking the elements that were matched so that the result is altered?


What I've tried:

1) I have tried removing the elements that are found to exist in both arrays in the way that follows, but unfortunately it doesn't do any good:

Example:

if (eachA === eachB) {
   a.splice.call(index, 1); // Removing the matched elements
   b.splice.call(jindex, 1);  // Removing the matched elements
   result[index] = true;
}

2) I've tried if (eachA === eachB && !result[index] && !result[jindex]) result[index] = true; as well thinking that, if result[index] and result[jindex] are already true, it means that a value in one array has been matched to a value in the other.


Code:

/* Main function */
function arrayEquals(a, b, result) {
  return (a === b && a !== null) || (a.length === b.length &&
    (function check(a, b, result) {
      /* Check equality between 'a' and 'b' arrays */
      a.forEach(function(eachA, index) {
        b.forEach(function(eachB, jindex) {
          if (eachA === eachB) result[index] = true;
          /* Handle objects */
          else if (isObject(eachA) && isObject(eachB))
            result[index] = objectEquals(a, b);
          /* Handle arrays */
          else if (isArray(eachA) && isArray(eachB))
            check(eachA, eachB, (result[index] = []));
          /* Turn all 'undefined' to 'false' */
          else result[index] = (!!result[index]) ? true : false;
        });
      });
      return result;
    })(a, b, (result = [])));
}

/* Usage */
var
  a = [1, 1, 2, ["a", "b"]],
  b = [1, 2, 2, ["a", "b"]];

console.log(arrayEquals(a, b)); /* returns: [true, true, true, [true, true]] */
                                /* should return: [true, false, true, [true, true]] */

/* Supplementary functions */
function isArray(array) {return !!array && array.constructor === Array;}

function isObject(object) {return !!object && object.constructor === Object;}


Checking Procedure:

var
   a = [1, 1, 2],
   b = [1, 2, 2];
  • For every index of the first array, we check all indices of the second array to one-by-one find a match.

  • For the first array, 1 (at index 0) matches 1 (at index 0) of the second array.

  • Then for the first array, 1 (at index 1) doesn't match any of the indices of the second array (1 at index 0 doesn't count, because we found a match earlier).

  • Lastly, for the first array, 2 (at index 2) matches 2 (at index 1) of the second array. RESULT: [true, false, true] | not equal.

Summary:

  • Each index must have a match, maybe the same index or some other.

  • If one index of the second array is used as a match for a previous index of the first array, it cannot be used as a match again.

  • The same element must exist again at another index to be used.

Image: The procedure

12
  • Why are results returned at second "Example:" not expected? Are you trying to check if the two array are exactly the same? Commented Sep 24, 2016 at 17:39
  • @guest271314 yes, that's what I'm trying. But how these elements are positioned inside the array is unforeseeable (random, not sorted). Commented Sep 24, 2016 at 17:48
  • "But how these elements are positioned inside the array is unforeseeable (random, not sorted)." What do you mean by "random"? Are you trying to check if element at a[index] is same as element at b[index]? Commented Sep 24, 2016 at 17:50
  • I mean the elements may be: a=[1, 2, 3] | b=[1, 2, 3] or a=[1, 3, 2] | b=[3, 2, 1]. The arrays are not sorted in any way. Commented Sep 24, 2016 at 17:53
  • Are you are trying to check if array b contains all elements of array a? At any index? Commented Sep 24, 2016 at 17:54

8 Answers 8

1

If guaranteed your arrays are in the same structure you may do as follows;

var a = [1, 1, 2, ["a", "b"]],
    b = [1, 2, 2, ["a", "b"]];

function arrayCompare(a,b){
 return a.map((e,i) => Array.isArray(e) ? arrayCompare(e, b[i])
                                        : e === b[i]);
}

console.log(arrayCompare(a,b))

Sign up to request clarification or add additional context in comments.

8 Comments

Your answer doesn't work for arrays that are not sorted.
@Angel Politis Sorted or not it compares index by index. It will check the indices to see if they map to the same primitive value. How exactly would you like to have the arrays compared? Could you please extend your question?
I extended my question @Redu. Two arrays that contain the same elements and have the same length should be equal regardless of the order of the elements inside the array.
@Angel Politis I understand what you mean but you don't express it correctly. In your terms "two arrays that contain the same elements and have the same length should be equal regardless of the order of the elements inside the array" makes [1,2,2] and ,[1,1,2] equal but i guess you need the element counts to be equal as well and just the order can change.
What do you mean when saying ''the element counts to be equal as well'' @Redu?
|
0

Instead of two foreach loop use only one and compare with other element's same index. Please check below snippet.

/* Main function */
function arrayEquals(a, b, result) {
  return (a === b && a !== null) || (a.length === b.length &&
    (function check(a, b, result) {
      /* Check equality between 'a' and 'b' arrays */
      a.forEach(function(eachA, index) {
        if (eachA === b[index]) result[index] = true;
          /* Handle objects */
          else if (isObject(eachA) && isObject(b[index]))
            result[index] = objectEquals(a, b);
          /* Handle arrays */
          else if (isArray(eachA) && isArray( b[index]))
            check(eachA,  b[index], (result[index] = []));
          /* Turn all 'undefined' to 'false' */
          else result[index] = (!!result[index]) ? true : false;
      });
      return result;
    })(a, b, (result = [])));
}

/* Usage */
var
  a = [1, 1, 2, ["a", "b"]],
  b = [1, 2, 2, ["a", "b"]];

console.log(arrayEquals(a, b)); /* returns: [true, true, true, [true, true]] */
                                /* should return: [true, false, true, [true, true]] */

/* Supplementary functions */
function isArray(array) {return !!array && array.constructor === Array;}

function isObject(object) {return !!object && object.constructor === Object;}

3 Comments

Your answer doesn't work for arrays that are not sorted.
Sort the array before you pass it in the function arguments. This snippet will check index by index so you can sort the arrays before passing the arguments and it will work fine.
I must not sort it.
0

This should work...

var
   a = [1, 2, 3, ["a", "b"]],
   b = [1, 2, 3, ["a", "b"]];

var
   c = [1, 1, 2, ["a", "b"]],
   d = [1, 2, 2, ["a", "b"]];

function arrayEquals(a, b) {
  if (a.length !== b.length) return false
  return a.map(function(el, i) {
    if (typeof a[i] === 'object' && a[i] instanceof Array) return arrayEquals(a[i], b[i])
    return a[i] === b[i] ? true : false
  })
}

console.log(arrayEquals(a, b))
console.log(arrayEquals(c, d))

1 Comment

Your answer doesn't work for arrays that are not sorted.
0

You are overcomplicating. You only need to map the longest array, and for each index compare if both elements (if any) are arrays. If they are, use recursion, otherwise compare normally.

function arrayCompare(arr1, arr2) {
  if(arr1.length < arr2.length) return arrayCompare(arr2, arr1);
  return arr1.map(function(item, idx) {
    if (idx >= arr2.length) return false;
    var b = Array.isArray(item) && Array.isArray(arr2[idx]);
    return (b ? arrayCompare : Object.is)(item, arr2[idx]);
  });
}
console.log(JSON.stringify(arrayCompare([1, 2, 3, ["a", "b"]], [1, 2, 3, ["a", "b"]])));
console.log(JSON.stringify(arrayCompare([1, 1, 2, ["a", "b"]], [1, 2, 2, ["a", "b"]])));
console.log(JSON.stringify(arrayCompare([1, 1, 2, 3], [1, 2, 2, ["a", "b"]])));
console.log(JSON.stringify(arrayCompare([1], [1, 2, 2, ["a", "b"]])));

6 Comments

What do you mean the longest array @Oriol? Both arrays should have the same length and exactly the same elements in them in any order.
@AngelPolitis "Both arrays should have the same length and exactly the same elements in them in any order." Why would /* should return: [true, false, true] */ be expected where input is var a = [1, 1, 2], b = [1, 2, 2];?
@guest271314 because the there is only one '1' in the second array. 1 is matched with 1, 2 is matched with 2 and finally the second 1 is matched with the second 2 and since they are not equal, the arrays aren't equal.
@AngelPolitis Well, this will work in a reasonable way even if the arrays have different lengths.
@Oriol Given further description of requirement at comments, second console.log() at stacksnippets should not return [true,false,true,[true,true]], where second array does not contain two 1s. Granted, this did not appear to be entirely clear at original Question.
|
0

Here is a fully recursive function that will compare the arrays:

function equality(array1, array2){
	if(array1.length === 0 ) return [];

	if(Array.isArray(array1[0]) && Array.isArray(array2[0])){
		return [equality(array1[0],array2[0])].concat(equality(array1.splice(1),array2.splice(1)));
	}
	return [array1[0] === array2[0]].concat(equality(array1.splice(1),array2.splice(1)));
}
console.log(equality([1,2,[3,3]], [1,2,[3,3]]));
console.log(equality([1,2,[3,2]], [1,2,[3,3]]));
console.log(equality([4,2,[3,3], 1], [1,3, 2,[3,3]]))

6 Comments

I'm not sure whether your answer works, as it has errors.
Errors where? Where are you running it? Just tested it in node.js chrome and firefox and its working.
If I put different values like this one: equality([4,2,[3,3], 1], [1,3, 2,[3,3]]), it throws an error. Specifically, array2.splice is not a function.
Yeah, like all your examples it depends on that both arrays have the same structure. But that is an easy fix! :)
@AngelPolitis that should do it! :)
|
0

Both arrays should have the same length and exactly the same elements in them in any order.

You can use Array.prototype.filter(), .length of returned array

var a = [1, 1, 2],
  b = [1, 2, 2],
  c = [1, 2, 1],
  d = [2, 1, 3],
  e = [2, 2, 1],
  f = [2, 1, 2];
    

var compare = (a, b) => 
      a.map(el =>
        b.filter(item => item === el).length ===
        a.filter(item => item === el).length
      );

console.log(compare(a, b), compare(c, d), compare(e, f))

12 Comments

compare(a, b) returns [false, false, false]. It should return [true, false, true], because there's one 1 and one 2 in each array, so we have two trues. Then since the remaining 1 and the remaining 2 are not equal this should give false.
@AngelPolitis "compare(a, b) returns [false, false, false]. It should return [true, false, true]" ? Why would [true, false, true] be returned? "because there's one 1 and one 2 in each array," There are not one 1 and one 2 in each array of a, b. array; var a = [1, 1, 2], b = [1, 2, 2]. You indicated that second array should have same amount of elements as first array, for that specific element of index
Because for every index of the first array, we check all indices of the second array to find a match. For the first array, 1 (index 0) matches 1* (index 0) of the second array. Then for the first array, 1 (index 1) doesn't match any of the indices of the second array (1 at index 0 doesn't count, because we found a match earlier). Lastly, for the first array, 2 (index 2) matches 2 (index 1) of the second array. RESULT: [true, false, true].
You are changing requirement. Earlier you mentioned that index does not matter "Both arrays should have the same length and exactly the same elements in them in any order." stackoverflow.com/questions/39679157/…. "Then for the first array, 1 (index 1) doesn't match any of the indices of the second array (1 at index 0 doesn't count, because we found a match earlier)" Where is this described at actual Question?
I said that the order the elements are placed inside their respective arrays is not a factor of inequality. I meant that two arrays don't have to be ordered the same way to be equal. Just contain the same elements and have the same length. The procedure that the checking happens is described above.
|
0

Perhaps, there are better ways than the following, but the following still gets the job done.

What the solution does:

Basically, what the following does is to change the values of eachA and eachB, if they are equal, to some Regular Expression.

To my knowledge, two regular expressions can't be equal, so in the wild scenario that the same regexp already exists in one of the arrays, it would result in false no matter what.

After all who puts a RegExp in an array and happens to want to check if that array is equal to another?!

Solution:

if (eachA === eachB) {
   result[index] = true;
   a[index] = b[jindex] = /RegExp/;
}

In addition:

In order to avoid any surprises, the inner forEach loop must be broken so that the when a match is found, it goes straight to the next character. So, we change the forEch**to a **for and we break it.

if (eachA === eachB) {
   result[index] = true;
   a[index] = b[jindex] = /RegExp/;
   break;
}

Code:

/* Main function */
function arrayEquals(a, b, result) {
  return (a === b && a !== null) || (a.length === b.length &&
    (function check(a, b, result) {
      /* Check equality between 'a' and 'b' arrays */
      a.forEach(function(eachA, index) {
        for (var jindex = 0, eachB = b[jindex]; jindex < b.length;
             jindex++, eachB = b[jindex]) {
          if (eachA === eachB) result[index] = true,
            a[index] = b[jindex] = /RegExp/, break;
          }
          /* Handle objects */
          else if (isObject(eachA) && isObject(eachB))
            result[index] = objectEquals(a, b);
          /* Handle arrays */
          else if (isArray(eachA) && isArray(eachB))
            check(eachA, eachB, (result[index] = []));
          /* Turn all 'undefined' to 'false' */
          else result[index] = (!!result[index]) ? true : false;
        }
      });
      return result;
    })(a, b, (result = [])));
}

/* Usage */
var
  a = [1, 2, 2, ["a", "b"]],
  b = [1, 2, 2, ["a", "b"]];

console.log(JSON.stringify(arrayEquals(a, b)));

/* Supplementary functions */
function isArray(array) {return !!array && array.constructor === Array;}

function isObject(object) {return !!object && object.constructor === Object;}

2 Comments

Note, if Answer resolves Question, you can accept your own Answer, see Can I answer my own question?
I know @guest271314, I've been on Stack Overflow for a while :p Still, though, I'm waiting for a better solution since mine is kind of meh from a programming perspective. Plus, there may actually be a way that two RegExps are equal that I have no idea of...
0

I'm doing C# and C++, not JavaScript, so I can't give you working code. But here's some pieces of pseudecode that should give you an idea of how it could work.
This is written as it comes out of my mind, typed on a mobile phone (terrible job) and untested. Hope it works for you! Please apologize any misspelling, which is likely causes by german auto-correction.

bool Compare (array a, array b, ref array result)
{
  Check whether arrays existiert and length is equal, return false if not;

  array<bool> b_used = new array(length (b));
  for (int indexa = 0 to length(a)-1)
  {
    if (a is value or object)
    { // compare against every b
      result[indexa] = false;
      for (int indexb = 0 to length(b)-1)
      {
        if (b_used[indexb])
          continue;  // this is the key: don't use an index twice!

        if (a[indexa] == b[indexb])
        {
          result[indexa] = true;
          b_used[indexb] = true;
          break;
        }
      }
    }
    Else if (array)
    ...
    And so on.

Notice that inner arrays are treated separately and not compared with values from outer arrays.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.