Javascript: Deep Comparison

zenwaichi picture zenwaichi · Jul 15, 2016 · Viewed 15.4k times · Source

I was checking this question Javascript Deep Comparison The solution of the question asker did not convince me so I tried to analyze the problem and came up with that

var obj = {here: 2};
console.log(deepEqual(obj, obj));
// → true
console.log(deepEqual(obj, {here: 1}));
// → false
console.log(deepEqual(obj, {here: 2}));
// → true
function deepEqual(a,b)
{
  if( (typeof a == 'object' && a != null) &&
      (typeof b == 'object' && b != null) )
  {
     var count = [0,0];
     for( var key in a) count[0]++;
     for( var key in b) count[1]++;
     if( count[0]-count[1] != 0) {console.log('1');return false;}
     for( var key in a)
     {
       if(!(key in b) || !deepEqual(a[key],b[key])) {console.log('2');return false;}
     }
     for( var key in b)
     {
       if(!(key in a) || !deepEqual(b[key],a[key])) {console.log('3');return false;}
     }  
  }
  console.log('a:'+a+' b:'+b);
  return a===b;
}
obj === { here:2 }

This code fails the last test ( console.log(deepEqual(obj, {here: 2})) ) but the logic of considering objects deeply equal if they have respectively equal keys and values despite being different instances in memory does not convince me. Is there's a problem with my 'solution' or the mistake lies in the presumptions of the exercise? Is the code mentioned in the question I linked valid?

Resources that hikinthru forgot to mention ( http://eloquentjavascript.net/04_data.html#exercise_deep_compare )

Answer

Frank Tan picture Frank Tan · Jul 17, 2016

"Deep equality", which is what the question you linked to is talking about, and "strict equality" are two different things. "Deep equality" means, as you said, "equal keys and equal values." "Strict equality" for objects means "same instance." Strict equality implies deep equality, but objects can be deeply equal without being strictly equal.

Your code is somewhat inefficient because you only need one loop, but it will behave correctly if you check a === b in an else block and return true after the for loops. This is because you should be handling objects separately from primitive values like strings and numbers. I've removed some logging for the sake of clarity, and I tried to keep your style.

var obj = {here: 2};
console.log(deepEqual(obj, obj));
// → true
console.log(deepEqual(obj, {here: 1}));
// → false
console.log(deepEqual(obj, {here: 2}));
// → true
console.log(obj === { here:2 });
// → false
function deepEqual(a,b)
{
  if( (typeof a == 'object' && a != null) &&
      (typeof b == 'object' && b != null) )
  {
     var count = [0,0];
     for( var key in a) count[0]++;
     for( var key in b) count[1]++;
     if( count[0]-count[1] != 0) {return false;}
     for( var key in a)
     {
       if(!(key in b) || !deepEqual(a[key],b[key])) {return false;}
     }
     for( var key in b)
     {
       if(!(key in a) || !deepEqual(b[key],a[key])) {return false;}
     }
     return true;
  }
  else
  {
     return a === b;
  }
}