0

I'm creating a function that checks whether two arrays are identical or not and I'm currently stuck at checking whether two objects (that could be inside an array) are identical.

To explain my code a little bit, I have a variable named eq that's returned when the function is over and it should contain either true for each element of the first array that exists in the second array or undefined if an element doesn't.

Also, I use a recursive IIFE to check if the object has sub-objects and the sub-objects are also identical. To check whether an array element is an object literal I use el.constructor === Object.

Without being 100% certain, I believe that I've done something wrong inside the for..in loops.

Code:

function equals(a, b) {
 return (a === b && a !== null) || (a.length === b.length && (function(a, b) {
 var eq = [];
 a.forEach(function(el1, index) {
 b.forEach(function(el2) {
 if (el1 === el2) eq[index] = true;
 else if (el1.constructor === el2.constructor && el1.constructor === Object) {
 /* a -> the object of the first array
 * b -> the object of the second array
 * c -> eq[index] then eq[index][i] provided how deep it goes */
 (function rec(a, b, c) {
 c = [];
 var i = 0;
 for (var prop in a) {
 for (var attr in b) {
 if (prop === attr && a[prop] === b[attr]) c[i] = true;
 else if (prop === attr && a[prop].constructor === b[attr].constructor
 && a[prop].constructor === Object) {
 rec(a[prop], b[attr], eq[index][i]);
 }
 }
 i++;
 }
 })(el1, el2, eq[index]);
 }
 });
 });
 return /*!~eq.indexOf(undefined);*/ eq;
 })(a, b));
}
/* Use */
var a = [1, {a: "a", b: "b" }, 4, 6],
 b = [{a: "a", b: "b"}, 1, 7, 6];
equals(a, b);


Example 1: (works fine for simple arrays)

var
 a = [1, 3, 4, 6],
 b = [3, 1, 7, 6];
equals(a, b); // returns: [true, true, undefined, true]

Example 2: (doesn't work for objects)

var
 a = [1, {a: "a", b: "b"}, 4, 6],
 b = [{a: "a", b: "b"}, 1, 7, 6];
equals(a, b); /* returns: [true, undefined, undefined, true]
 SHOULD return: [true, [true, true], undefined, true] */

Any help would be greatly appreciated.

asked Sep 22, 2016 at 12:23
4
  • I'm pretty sure a function that compares two arrays already exists in one of the numerous libraries and frameworks. Why re-invent the wheel? Commented Sep 22, 2016 at 12:25
  • It's an exercise for my college @HubertGrzeskowiak :) Commented Sep 22, 2016 at 12:26
  • Ah okay. And are you sure you want to compare the arrays ignoring the elements' order? Commented Sep 22, 2016 at 12:27
  • Yep, that's what the exercise says, but still if we exclude objects the function works without sorting the arrays. There's going to be a problem with duplicate values, but I have a solution for that. Commented Sep 22, 2016 at 12:28

3 Answers 3

2

I think the problem lies in your eq[index] being passed to the comparator function. This doesn't pass a reference, but simply an undefined value. By setting c = [] inside the function you are overriding that undefined by a new array. There is no connection to eq whatsoever. Restructure your code so that you either create the array in eq[index] outside rec and pass it in (c). Or make the function rec return a value.

answered Sep 22, 2016 at 12:38
Sign up to request clarification or add additional context in comments.

1 Comment

Thanks @Hubert, your answer gave me some good food for thought. I found a way around by declaring eq[index][i] as an array at the time it's passed to rec.
0

Here is a take on it. The code could use some shortening and I didn't test it excessively but the recursion seems to work for nestled content of any depth.

var
 a = [1, {a: "a", b: "b"}, 4, [1,{c:5,d:[1,2]}], 6],
 b = [{a: "a", b: "b"}, [1,{c:5,d:[1,2]}], 1, 4, 6];
/*
 * returns 'true' if arrays are the same
 *
 * depends on 'checkObjects'
 */
function checkArrays(arrayA, arrayB) {
 if (arrayA === arrayB) return true;
 if (arrayA === null || arrayB === null) return false;
 if (arrayA.length !== arrayB.length) return false;
 // since the order doesn't matter we sort the arrays
 arrayA.sort();
 arrayB.sort();
 var arrayLength = arrayA.length; // stored for performance
 for (var i = 0; i<arrayLength; i++) {
 // if same value we continue through the array
 // else they are not the same and we can stop
 if (typeof arrayA[i] === 'object' && typeof arrayB[i] === 'object') {
 if (!checkObjects(arrayA[i], arrayB[i])) {
 return false;
 }
 } else if (typeof arrayA[i] === 'array' && typeof arrayB[i] === 'array') {
 if (!checkArrays(arrayA[i], arrayB[i])) {
 return false;
 }
 } else if (arrayA[i] !== arrayB[i]) {
 return false;
 }
 }
 // if we get here the values are equal
 return true;
}
/*
 * returns 'true' if objects are the same
 *
 * depends on 'checkArrays'
 */
function checkObjects(objectA, objectB) {
 if (objectA === objectB) return true;
 if (Object.keys(objectA).length !== Object.keys(objectB).length) return false;
 var keys = Object.keys(objectA),
 numberOfKeys = keys.length; // stored for performance
 for (var i=0; i<numberOfKeys; i++) {
 if (!(keys[i] in objectB)) {
 return false; // objects don't have the same keys
 } else {
 if (!(objectA[keys[i]] === objectB[keys[i]])) {
 // if same key-value-pairs exist we continue through the array
 // else they are not the same and we can stop
 if (typeof objectA[keys[i]] === 'array' && typeof objectB[keys[i]] === 'array') {
 if (!checkArrays(objectA[keys[i]], objectB[keys[i]])) {
 return false;
 }
 } else if (typeof objectA[keys[i]] === 'object' && typeof objectB[keys[i]] === 'object') {
 if (!checkObjects(objectA[keys[i]], objectB[keys[i]])) {
 return false;
 }
 } else {
 return false;
 }
 }
 }
 }
 // if we get here the key-value-pairs are equal
 return true;
}
console.log(checkArrays(a, b));

answered Sep 22, 2016 at 13:37

Comments

0

As Hubert Grzeskowiak mentions in his answer the problem lies in c not having any connection whatsoever with eq[index].

A quick solution I came up with that solves the issue is deleting c = [] and declaring eq[index][i] as an array at the time it's passed to rec as shown in the code below:

Code: (Just the IIFE)

(function rec(a, b, c) {
 var i = 0;
 for (var prop in a) {
 for (var attr in b) {
 console.log(i)
 if (prop === attr && a[prop] === b[attr]) c[i] = true;
 else if (prop === attr && a[prop].constructor === b[attr].constructor &&
 a[prop].constructor === Object) {
 rec(a[prop], b[attr], (eq[index][i] = [])); // ← instead of eq[index][i]
 }
 }
 i++;
 }
})(eachA, eachB, eq[index]);
answered Sep 22, 2016 at 13:01

Comments

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

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.