Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
JavaScript object deep comparison. Comparing x === y, where x and y are values, return true or false. Comparing x === y, where x and y are objects, returns true if x and y refer to the same object. Otherwise, returns false even if the objects appear identical. Here is a solution to check if two objects are the same.
//Primitive Type Comparison
var a = 1;
var b = 1;
var c = a;
console.log(a == b); //true
console.log(a === b); //true
console.log(a == c); //true
console.log(a === c); //true
//Object comparison
var a = { blah: 1 };
var b = { blah: 1 };
var c = a;
console.log(a == b); //false
console.log(a === b); //false
console.log(a == c); //true
console.log(a === c); //true
//How To Compare Object Values
var a = { blah: 1 };
var b = { blah: 1 };
var c = a;
var d = { blah: 2 };
Object.compare = function (obj1, obj2) {
//Loop through properties in object 1
for (var p in obj1) {
//Check property exists on both objects
if (obj1.hasOwnProperty(p) !== obj2.hasOwnProperty(p)) return false;
switch (typeof (obj1[p])) {
//Deep compare objects
case 'object':
if (!Object.compare(obj1[p], obj2[p])) return false;
break;
//Compare function code
case 'function':
if (typeof (obj2[p]) == 'undefined' || (p != 'compare' && obj1[p].toString() != obj2[p].toString())) return false;
break;
//Compare values
default:
if (obj1[p] != obj2[p]) return false;
}
}
//Check object 2 for any extra properties
for (var p in obj2) {
if (typeof (obj1[p]) == 'undefined') return false;
}
return true;
};
console.log(Object.compare(a, b)); //true
console.log(Object.compare(a, c)); //true
console.log(Object.compare(a, d)); //false
@granttaylor0710

This comment has been minimized.

Copy link

@granttaylor0710 granttaylor0710 commented Apr 1, 2016

I am confused how I can use this when the object is more complex involving object as element.

Please let me know if you have solution.

@MikeWarren2014

This comment has been minimized.

Copy link

@MikeWarren2014 MikeWarren2014 commented Aug 11, 2016

The code is recursive. Try it.

@JulianNicholls

This comment has been minimized.

Copy link

@JulianNicholls JulianNicholls commented Feb 24, 2017

This is exactly what I needed. Thank you.

@jerrykrinock

This comment has been minimized.

Copy link

@jerrykrinock jerrykrinock commented Feb 27, 2017

Works for me. Thank you, nicbell. Here is some test code:

` /* We shall define three objects: a,b,c, then compare them using nicbell's Object.compare() function. Note that a is the same as b, but c is different (in numbers.decimal.hundreds) */

	let a = {
	  foods:{
	    fruits:["orange", "lemon"]
	  },
	  numbers:{
	    "Decimal": {
	      tens:[40, 50, 20],
	      hundreds:[300, 500]
	    },
	    "Roman": {
	      small:["I", "VII", "IX"],
	      hundreds:[300, 500]
	    },
	  },
	  bikes:["recumbent", "upright"]
	};
	let b = {
	  foods:{
	    fruits:["orange", "lemon"]
	  },
	  numbers:{
	    "Decimal": {
	      tens:[40, 50, 20],
	      hundreds:[300, 500]
	    },
	    "Roman": {
	      small:["I", "VII", "IX"],
	      hundreds:[300, 500]
	    },
	  },
	  bikes:["recumbent", "upright"]
	};
	let c = {
	  foods:{
	    fruits:["orange", "lemon"]
	  },
	  numbers:{
	    "Decimal": {
	      tens:[40, 50, 20],
	      hundreds:[300, 700]
	    },
	    "Roman": {
	      small:["I", "VII", "IX"],
	      large:["MCXVII", "MCXXVIII", "MMVIII"]
	    },
	  },
	  bikes:["recumbent", "upright"]
	};
	
	console.log("Comparing a,b result is ", Object.compare(a,b));  // true
	console.log("Comparing a,c result is ", Object.compare(a,c));  // false
	console.log("Comparing b,c result is ", Object.compare(b,c));  // false

`

@proworkdev

This comment has been minimized.

Copy link

@proworkdev proworkdev commented Mar 21, 2017

[{
"lat": "-31.96012295",
"lon": "115.81055528",
"elevation": "11.7",
"elevationChange": -0.1,
"time": 1484527078
}, {
"lat": "-31.96013326",
"lon": "115.81051413",
"elevation": "11.7",
"elevationChange": 0,
"time": 1484527079
}, {
"lat": "-31.96015966",
"lon": "115.81048152",
"elevation": "11.7",
"elevationChange": 0,
"time": 1484527080
}, {
"lat": "-31.96019663",
"lon": "115.81046426",
"elevation": "11.7",
"elevationChange": 0,
"time": 1484527081
}, {
"lat": "-31.96024138",
"lon": "115.8104542",
"elevation": "11.7",
"elevationChange": 0,
"time": 1484527082
}
}]

I have two two objects as above and want to compare the obect lat and long. How i can do this??

@ghost

This comment has been minimized.

Copy link

@ghost ghost commented May 7, 2017

Unfortunately, it is not "a solution".

Your Object.compare throws an error when objects are circular, even if they are equal indeed.
Consider the code:

let first = { test: "test" };
let second = { test: "test" };

first.self = first;
second.self = second;

Object.compare(first, second); // Uncaught RangeError: Maximum call stack size exceeded

When the issue is resolved, try this:

// (omitted)

first.other = second;
second.other = first;

Object.compare(first, second);
@Anilk1sagar

This comment has been minimized.

Copy link

@Anilk1sagar Anilk1sagar commented Aug 12, 2017

Can anyone tell me why objects cannot be compared? what is the reason?

@D-Nice

This comment has been minimized.

Copy link

@D-Nice D-Nice commented Sep 8, 2017

@Anilk1sagar
It's not a question that they can't be compared, they are compared, but by their reference in memory, rather than properties. Just because an object shares the same properties and values at the moment, does not mean it's the same object, does it?

@giffeler

This comment has been minimized.

Copy link

@giffeler giffeler commented Oct 6, 2017

Why don't you just use JSON.stringify on both objects and compare the strings afterwards...?

@avionbg

This comment has been minimized.

Copy link

@avionbg avionbg commented Oct 11, 2017

Because of this :
JSON.stringify({a:1, b:2}) == JSON.stringify({b:2, a:1}) === false

@Kenigmatic

This comment has been minimized.

Copy link

@Kenigmatic Kenigmatic commented Nov 27, 2017

To support cases where obj1 and/or obj2 may be undefined, I changed:

if (obj1.hasOwnProperty(p) !== obj2.hasOwnProperty(p)) return false;
... to ....
if (obj2 === undefined || obj1.hasOwnProperty(p) !== obj2.hasOwnProperty(p)) return false;

... and ...

if (typeof (obj1[p]) == 'undefined') return false;
... to ...
if (obj1 === undefined || typeof (obj1[p]) == 'undefined') return false;

@michaelwooley

This comment has been minimized.

Copy link

@michaelwooley michaelwooley commented Dec 20, 2017

I found the same issue as @Kenigmatic but thought it would be a simpler solution to just add a line at the top:

Object.compare = function (obj1, obj2) {
  // Check if 'objects' are of same type
  if (typeof(obj1) !== typeof(obj2)) return false;

  //Loop through properties in object 1
  for (var p in obj1) {
[....]

This may be necessary if the method is called recursively. Here's an example of case that this addresses:

// Would fail on recursion.
obj1a = {a: {aa: 1}, b: 1};
obj2a = {a: undefined, b: 1};
// Would not fail on recursion.
obj1b = {a: undefined, b: 1};
obj2b = {a: {aa: 1}, b: 1};

The original code would do well on the second example but not the first because it assumes that it is always passed two @objects.

@cubeDhuang

This comment has been minimized.

Copy link

@cubeDhuang cubeDhuang commented Jan 5, 2018

What if an element of the object is null?
If I use { something: null } in a comparison, then it will cause an error.

We should add:

if (obj1[p] === null) continue;

This will prevent deep comparison from trying to access properties of null.

@sexygeoff

This comment has been minimized.

Copy link

@sexygeoff sexygeoff commented Jan 12, 2018

Yes I have the same problem as @cubeDhuang as well as with 0 being equal to ''. I ended up with this:

Object.compare = function (obj1, obj2) {
    //Loop through properties in object 1
    for (var p in obj1) {
        //Check property exists on both objects
        if (obj1.hasOwnProperty(p) !== obj2.hasOwnProperty(p)) return false;

        if(obj1[p]===null && obj2[p]!==null) return false;
        if(obj2[p]===null && obj1[p]!==null) return false;

        switch (typeof (obj1[p])) {
            //Deep compare objects
            case 'object':
                if (!Object.compare(obj1[p], obj2[p])) return false;
                break;
            //Compare function code
            case 'function':
                if (typeof (obj2[p]) == 'undefined' || (p != 'compare' && obj1[p].toString() != obj2[p].toString())) return false;
                break;
            //Compare values
            default:
                if (obj1[p]==='' && obj2[p]!=='') return false;
                if (obj2[p]==='' && obj1[p]!=='') return false;
                if (obj1[p] != obj2[p]) return false;
        }
    }

    //Check object 2 for any extra properties
    for (var p in obj2) {
        if (typeof (obj1[p]) == 'undefined') return false;
    }
    return true;
};
@marekkaczkowski

This comment has been minimized.

Copy link

@marekkaczkowski marekkaczkowski commented May 22, 2018

In my case I don't care about undef, null and zeros so simple as
String(Object.keys(a).sort()) === String(Object.keys(b).sort())

@vvassim-baghdadi

This comment has been minimized.

Copy link

@vvassim-baghdadi vvassim-baghdadi commented Jun 10, 2018

It fails when the object contains a Set

@imadbz

This comment has been minimized.

Copy link

@imadbz imadbz commented Jun 19, 2018

@nicbell I made it compare non object types too so you can use it as drop in replacement for '===' that supports deep object comparisons. https://gist.github.com/imadbz/333d4aa9dd6cec1f8d6f58faccab9d19
Thanks for the function btw. You rock!

@k7moorthi

This comment has been minimized.

Copy link

@k7moorthi k7moorthi commented Jul 26, 2018

Fails with the below example:

var oldObj = {
  "name": "John",
  "age": 30,
  "childs": ["Smith", "Steve", "Trent"],
  "vechicals": {
    "twoWheelers": ["Honda", "Bajaj", "Suzuki"],
    "fourWheelers": {
      "cars": {
        "count": 3,
        "details": [{
          "make": "bmw",
          "model": "123"
        }, {
          "make": "Ford",
          "model": "234"
        }]
      },
      "vans": {
      "count": 5
      }
    }
  }
};

var newObj = {
  "age": 30,
  "name": "John",
  "childs": ["Smith", "Steve", "Trent"],
  "vechicals": {
    "twoWheelers": ["Honda", "Bajaj", "Suzuki"],
    "fourWheelers": {
      "cars": {
        "count": 3,
        "details": [{
          "make": "Ford",
          "model": "234"
        }, {
          "make": "bmw",
          "model": "123"
        }]
      },
      "vans": {
      "count": 5
      }
    }
  }
};
@nikitat1994

This comment has been minimized.

Copy link

@nikitat1994 nikitat1994 commented Jul 30, 2018

@k7moorthi order is important in arrays, so that failure is valid.

@ajaygt

This comment has been minimized.

Copy link

@ajaygt ajaygt commented Oct 24, 2018

This code snippet worked for me.
var numOfCalls =0;
function requestValidator (input, response) {
console.log("input.length "+Object.keys(input));

var maxCallsPermitted = Object.keys(input).length;
var failureWatcher = false;
for (var key in input) {
   switch (typeof(input[key])) {

      case "object": if (!(response.hasOwnProperty(key)) || !(requestValidator(input[key],response[key]))) {
                        console.log("response has the key to be validated ==>"+response.hasOwnProperty(key));
                        failureWatcher = true;
                        return false;
                     }
                     break;
      case "number" :
      case "boolean" :
      case "string" :
      default:
                    if (input[key] != response[key]) {
                         console.log("Values in the json do not match for "+key+" Expected value "+input[key]+" Result Value "+response[key]);
                         failureWatcher = true;
                         return false;
                      } else {
                         try
{
    JSON.stringify(input);
}
catch(e)
{
    if(e.includes("Converting circular structure to JSON"));
   {
                         numOfCalls = numOfCalls +1;
                         if(numOfCalls > maxCallsPermitted){
                            return true;
                         }
   }
}
                         
                         console.log("Values inside the two json match for key "+key)
                      }

}
}
if (failureWatcher) {
return false
} else {
return true;
}
}

@AtomLogan

This comment has been minimized.

Copy link

@AtomLogan AtomLogan commented Jan 23, 2019

PERFECT, THANKS!

@JLuitjens

This comment has been minimized.

Copy link

@JLuitjens JLuitjens commented Jan 31, 2019

doesn't work with Date object, fallback into function comparison which are equal

@Nas10ka

This comment has been minimized.

Copy link

@Nas10ka Nas10ka commented Feb 16, 2019

In the Object.compare method is absent the case of Array. If you compare two identical arrays with == or === the result will be false.
const arr1 = [1, 2, 3]; const arr2 = [1, 2, 3]; arr1 === arr2 // false

@ColinRyan

This comment has been minimized.

Copy link

@ColinRyan ColinRyan commented May 9, 2019

is this what you're trying to make? https://ramdajs.com/docs/#equals

@drix

This comment has been minimized.

Copy link

@drix drix commented May 20, 2019

keeping simple:

Object.compare = (a, b) => JSON.stringify(a) === JSON.stringify(b);

It ignores functions, but work for most scenarios

@IAfanasovMob

This comment has been minimized.

Copy link

@IAfanasovMob IAfanasovMob commented May 23, 2019

keeping simple:

Object.compare = (a, b) => JSON.stringify(a) === JSON.stringify(b);

It ignores functions, but work for most scenarios

and fails to compare properly objects with the same fields in a different order

@drix

This comment has been minimized.

Copy link

@drix drix commented May 24, 2019

keeping simple:
Object.compare = (a, b) => JSON.stringify(a) === JSON.stringify(b);
It ignores functions, but work for most scenarios

and fails to compare properly objects with the same fields in a different order

Solvable by doing:

Object.compare = (a, b) => JSON.stringify(Object.entries(a).sort()) === JSON.stringify(Object.entries(b).sort())

@IAfanasovMob

This comment has been minimized.

Copy link

@IAfanasovMob IAfanasovMob commented May 24, 2019

keeping simple:
Object.compare = (a, b) => JSON.stringify(a) === JSON.stringify(b);
It ignores functions, but work for most scenarios

and fails to compare properly objects with the same fields in a different order

Solvable by doing:

Object.compare = (a, b) => JSON.stringify(Object.entries(a).sort()) === JSON.stringify(Object.entries(b).sort())

not really solve for objects with nested objects

@drix

This comment has been minimized.

Copy link

@drix drix commented May 24, 2019

keeping simple:
Object.compare = (a, b) => JSON.stringify(a) === JSON.stringify(b);
It ignores functions, but work for most scenarios

and fails to compare properly objects with the same fields in a different order

Solvable by doing:
Object.compare = (a, b) => JSON.stringify(Object.entries(a).sort()) === JSON.stringify(Object.entries(b).sort())

not really solve for objects with nested objects

Solvable by doing:

Object.compare = (a, b) => { let s = o => Object.entries(o).sort().map(i => { if(i[1] instanceof Object) i[1] = s(i[1]); return i }) return JSON.stringify(s(a)) === JSON.stringify(s(b)) }

Not simple anymore =( *still quite smaller than the proposed solutions

@quantuminformation

This comment has been minimized.

Copy link

@quantuminformation quantuminformation commented Jul 10, 2019

Can you add a max call depth param please?

@nicbell

This comment has been minimized.

Copy link
Owner Author

@nicbell nicbell commented Jul 12, 2019

@quantuminformation this isn't a library it's just Gist. When you use it you are welcome to add a max call depth param.

@tannerjt

This comment has been minimized.

Copy link

@tannerjt tannerjt commented Sep 23, 2019

Seems to fail with null values

@nicbell

This comment has been minimized.

Copy link
Owner Author

@nicbell nicbell commented Sep 23, 2019

@tannerjt this isn't a library it's just a Gist. When you use it you are welcome to add a null check.

@tannerjt

This comment has been minimized.

Copy link

@tannerjt tannerjt commented Sep 23, 2019

Thanks, @nicbell. I added it to a project I was working on and just letting others know who attempt to use this and run into the same issue. Thanks for posting this as a GIST, very helpful.

@dbuzzin

This comment has been minimized.

Copy link

@dbuzzin dbuzzin commented Mar 26, 2020

If anyone is concerned about circular objects, I think this should solve this issue for most cases.

Object.compare = function(obj1, obj2) {
    const top1 = obj1;
    const top2 = obj2;

    const run = (obj1, obj2) => {

        // The rest of the original code...

        switch(typeof obj1[p]) {
            case "object":
                if((Object.is(top1, obj1[p]) && Object.is(top2, obj2[p]))
                || (Object.is(top2, obj1[p]) && Object.is(top1, obj2[p]))
                || (Object.is(obj1, obj2))) continue;
                if (!run(obj1[p], obj2[p])) return false;
                break;
        }

        // More code...

    }
    return run(obj1, obj2);
}

For Internet Explorer there is a polyfill for Object.is() or this would probably work fine because they are just referencing the object at the top level.

switch(typeof obj1[p]) {
    case "object":
        if((top1 === obj1[p] && top2 === obj2[p])
        || (top2 === obj1[p] && top1 === obj2[p])
        || (obj1 === obj2)) continue;
        if (!run(obj1[p], obj2[p])) return false;
        break;
    }
    // etc ...
}
@dbuzzin

This comment has been minimized.

Copy link

@dbuzzin dbuzzin commented Mar 26, 2020

Class gist btw mate :) it's definitely inspired me

@nicbell

This comment has been minimized.

Copy link
Owner Author

@nicbell nicbell commented Apr 26, 2020

@dbuzzin glad you found it helpful. 🙏

@gleissonmattos

This comment has been minimized.

Copy link

@gleissonmattos gleissonmattos commented Jul 1, 2020

Hey guys. This is unnecessary. We can only:

const foo = { a: 1, b: { single: 11 }};
const bar = {
  a: 1,
  b: {
    single: 11
  }
};

JSON.stringify(foo) === JSON.stringify(bar); // true
@edwinro

This comment has been minimized.

Copy link

@edwinro edwinro commented Jul 9, 2020

I understood JSON.stringify() is not very performant, and misses if child objects are in a different order.

I've updated the initial code to support arrays (order should be respected), and also 'null vs {}' checks.

No warranties on the code ;)

function compare (obj1, obj2) {

        //check type at start
        if (typeof (obj1) !== typeof (obj2)) return false;
        if (Array.isArray(obj1) !== Array.isArray(obj2)) return false;

        //case no children
        if (obj1 && Object.keys(obj1).length === 0) return (obj1 === obj2);
        if (obj1 == null) return (obj1 === obj2); //case if obj1 is nullish

        //in case of an array
        if (Array.isArray(obj1)) {
            for (let i = 0; i < obj1.length; i++) {
                if (!compare(obj1[i], obj2[i])) return false;
            }

            return true;
        } else {

            //general object case
            //Loop through properties in object 1
            for (var p in obj1) {
                //Check property exists on both objects
                if (obj1.hasOwnProperty(p) !== obj2.hasOwnProperty(p)) return false;

                switch (typeof (obj1[p])) {
                    //Deep compare objects
                    case 'object':
                        if (!compare(obj1[p], obj2[p])) return false;
                        break;
                    //Compare function code
                    case 'function':
                        if (typeof (obj2[p]) == 'undefined' || (p != 'compare' && obj1[p].toString() != obj2[p].toString())) return false;
                        break;
                    //Compare values
                    default:
                        if (obj1[p] !== obj2[p]) return false;
                }
            }

            //Check object 2 for any extra properties
            for (var p in obj2) {
                if (typeof (obj1[p]) == 'undefined') return false;
            }

            return true;
        }
};
@meetdheeraj

This comment has been minimized.

Copy link

@meetdheeraj meetdheeraj commented Aug 6, 2020

function ObjectCompare(obj1, obj2) {
     return !( obj1 < obj2 || obj1 > obj2);
}

Worked for me

@clibu

This comment has been minimized.

Copy link

@clibu clibu commented Aug 17, 2020

@edwinro Thx for you compare() code. I have found a couple of issues:

// case no children
if (obj1 && Object.keys(obj1).length === 0) return (obj1 === obj2);

does not work correctly. ex.

const obj1 = {}, obj2 = {}
console.log( obj1 === obj2 )  - false

Changed to: return Object.keys( obj2 ).length === 0 fixes this.

Next:

// case if obj1 is nullish
if (obj1 == null) return (obj1 === obj2); 

fails for const obj1 = null, obj2 = undefined

Changed to: return (obj1 == obj2); fixes this. ie. Double equals vs triple equals.

Next:

            //Check object 2 for any extra properties
            for (var p in obj2) {
                if (typeof (obj1[p]) == 'undefined') return false;
            }

does not work correctly if obj2[p] is also undefined:
Fix is to change: if (typeof (obj1[p]) == 'undefined') return false; to if ( obj1[p] === undefined && obj1[2] !== undefined ) return false;

@edwinro

This comment has been minimized.

Copy link

@edwinro edwinro commented Aug 17, 2020

@clibu thank you for your corrections.

Updated thanks to your remarks

    function compare (obj1, obj2) {

        //check type at start
        if (typeof (obj1) !== typeof (obj2)) return false;
        if (Array.isArray(obj1) !== Array.isArray(obj2)) return false;

        //case no children
        if (obj1 && Object.keys(obj1).length === 0) return (Object.keys( obj2 ).length === 0);
        if (obj1 == null) return (obj1 === obj2); //case if obj1 is nullish

        //in case of an array
        if (Array.isArray(obj1)) {
            for (let i = 0; i < obj1.length; i++) {
                if (!compare(obj1[i], obj2[i])) return false;
            }

            return true;
        } else {

            //general object case
            //Loop through properties in object 1
            for (var p in obj1) {
                //Check property exists on both objects
                if (obj1.hasOwnProperty(p) !== obj2.hasOwnProperty(p)) return false;

                switch (typeof (obj1[p])) {
                    //Deep compare objects
                    case 'object':
                        if (!compare(obj1[p], obj2[p])) return false;
                        break;
                    //Compare function code
                    case 'function':
                        if (typeof (obj2[p]) == 'undefined' || (p != 'compare' && obj1[p].toString() != obj2[p].toString())) return false;
                        break;
                    //Compare values
                    default:
                        if (obj1[p] !== obj2[p]) return false;
                }
            }

            //Check object 2 for any extra properties
            for (var p in obj2) {
                        if ( obj1[p] === undefined && obj2[p] !== undefined ) return false;
            }

            return true;
        }
};
@clibu

This comment has been minimized.

Copy link

@clibu clibu commented Aug 18, 2020

@edwinro Thanks for the update. I snuck in an edit re. an issue with the nullish test which you understandably missed. ie. == vs ===

@edwinro

This comment has been minimized.

Copy link

@edwinro edwinro commented Aug 18, 2020

@edwinro Thanks for the update. I snuck in an edit re. an issue with the nullish test which you understandably missed. ie. == vs ===

@clibu thanks. The if (typeof (obj1) !== typeof (obj2)) return false; catches already the case where obj 1= null and obj2 = undefined. The if statement then returns false.

typeof (null) !== typeof (undefined)
true

The question is , do you want to let the compare function return false if one object is undefined and the other is null? I take the assumption one does indeed wants to detect this difference. ( undefined means a variable has been declared but has not yet been assigned a value, whereas null is an assignment value).

If not, your code suggestion can be applied, but should be put as a first line. Also, the last check if ( obj1[p] === undefined && obj2[p] !== undefined ) return false; would then be replace by if ( obj1[p] === undefined && obj2[p] != undefined ) return false; to also account for null children vs undefined children.

@clibu

This comment has been minimized.

Copy link

@clibu clibu commented Aug 18, 2020

@edwinro I had picked up on the if (typeof (obj1) !== typeof (obj2)) return false; handling this and I agree that is what you'd want.

The nullish comment threw me as nullish means null or undefined. I'm not sure why this test is there?

FYI I'm in Australia, a bit of a time difference. 😀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.