Deep Compare JavaScript function

3.5k views Asked by At

I'm working my way through the 3rd edition of Eloquent JavaScript and although I've seen one or two various answers on SO that seem almost identical in execution logic to mine that work mine just doesnt seem to no matter how I tweek it.

THE GOAL: create a deep comparison function that can compare two objects and determine based on their properties if theyre different instances of the same type of object (same keys and values) regardless of reference...

Can anyone spot the bug in my code?

function deepEqual(a,b){
  if((typeof a=='object'&& a!=null)&&(typeof b=='object'&& b!=null)){
   if(Object.keys(a).length != Object.keys(b).length){return false}    
    for(let key in a){
        if(a[key]==b[key]){
          if(!deepEqual(a[key],b[key])){return false}
        }else{
          return false
        }
    }
   return true
  }else if(a!==b){return false}

  else{return true}
}

var obj = {here: {is: "an"}, object: 2};
console.log(deepEqual(obj, obj));
// → true (reads true currently)
console.log(deepEqual(obj, {here: 1, object: 2}));
// → false (reads false currently)
console.log(deepEqual(obj, {here: {is: "an"}, object: 2}));
// → true (reads false currently)
3

There are 3 answers

2
Kosh On BEST ANSWER

The bug was here:

if (a[key] == b[key])

if they're objects the condition may return false even if they're "equal".

function deepEqual(a, b) {
  if (a && b && typeof a == 'object' && typeof b == 'object') {
    if (Object.keys(a).length != Object.keys(b).length) return false;
    for (var key in a) if (!deepEqual(a[key], b[key])) return false;
    return true;
  } else return a === b
}

var obj = {here: {is: "an"}, object: [2]};
console.log(deepEqual(obj, obj));
// → true 
console.log(deepEqual(obj, {here: 1, object: 2}));
// → false 
console.log(deepEqual(obj, {here: {is: "an"}, object: [2]}));
// → true 

0
Amresh Kumar On

You can use below function to deep compare values

 function deepCompare(a,b){
       // shallow check
        if (a === b) return true;
    
        // determining types
        const typeA = Object.prototype.toString.call(a);
        const typeB = Object.prototype.toString.call(b);
    
        // if types are not equal
        if (typeA != typeB) return false;
    
        // comparing if object
        if (typeA === '[object Object]') {
            if (Object.keys(a) !== Object.keys(b)) return false;
            for (let key in a)
                if (!deepCompare(a[key], b[key])) return false;
            return true;
        }
    
        // comparing if array
        else if (typeA === '[object Array]') {
            if (a.length !== b.length) return false;
            for (let i = 0; i < a.length; i++)
                if (!deepCompare(a[i], b[i])) return false;
            return true;
        }
    
        // comparing if map 
        else if (typeA === "[object Map]") {
            if (a.keys().length !== b.keys().length) return false;
            for (let key of a.keys())
                if (!deepCompare(a.get(key), b.get(key))) return false;
            return true;
        }
      
        // comparing if sets
        else if (typeA === '[object Set]') {
            let x = [...a], y = [...b];
            return deepCompare(x, y);
        }
      
        // null & undefined checks
        else if (typeA === '[object Undefined]' || typeA === '[object Null]')
            return true;
    
        // nothing matched 
        return false;
    }

let a=[1,'a',{x:[1,2,3], y: new Map([['a',1], ['b', new Set([1,2,3])]])}]
let b=[1,'a',{x:[1,2,3], y: new Map([['a',1], ['b', new Set([1,2,3])]])}]

console.log(deepCompare(a,b)) // true

let c=[1,'a',{x:[1,2,3], y: new Map([['a',1], ['b', new Set([1,2,3])]])}]
let d=[1,'a',{x:[1,2,3], y: new Map([['a',1], ['b', 2]])}]

console.log(deepCompare(c, d)) //false
0
K.F On

Looks like you just need to tweak your for-loop logic to catch objects like so (using your own logic):

for(let key in a){
  let aValue = a[key]
  let bValue = b[key]

  if (typeof aValue == 'object' && typeof bValue == 'object') {
    // catches nested objects and recursively assesses deep equality
    if(!deepEqual(aValue, bValue)) return false
  }
  else if (aValue !== bValue) {
    // if the values are not objects, catches if they are not equal
    return false
  }
}

You can play with this, here: https://jsbin.com/wotapezuwo/edit?js,console