How can I determine equality for two JavaScript objects?
Asked Answered
P

77

994

A strict equality operator will tell you if two object types are equal. However, is there a way to tell if two objects are equal, much like the hash code value in Java?

Stack Overflow question Is there any kind of hashCode function in JavaScript? is similar to this question, but requires a more academic answer. The scenario above demonstrates why it would be necessary to have one, and I'm wondering if there is any equivalent solution.

Pariah answered 14/10, 2008 at 13:41 Comment(15)
Also look into this question https://mcmap.net/q/36773/-object-comparison-in-javascript-duplicate/1671639Outstand
Note that, even in Java, a.hashCode() == b.hashCode() does not imply that a is equal to b. It's a necessary condition, not a sufficient one.Houselights
If you HAVE to compare objects in your code than you are probably writing your code wrong. The better question might be: "How can I write this code so I don't have to compare objects?"Redblooded
Try object-equals package.Bristle
@Redblooded can you please explain yourself?...Guernsey
@ElMac I can't speak directly for the person but I do agree with the statement and my thought process is that very often JS objects are fairly big. You rarely have something with very few properties like person = { name: "fred", age: 42 }. If you do and you have to search them by exact equality, it seems like a waste. Most often your plain objects will still have a lot of properties - and one either one is uinique or you want one to be unique, e.g., ID of some sort. You can then search by that and you don't need to check if every single thing matches.Niblick
Most of the commenters here are correct in that this is not a very good method. Since this post I have played around with this method a lot, with varied success. Ultimately it isn't a very good method for object comparison, and the amount of extra code required to get it to work mostly properly makes it not worth it (just use a simple deep object comparator instead)Redblooded
Try deep-equal NPM packageBing
jsben.ch/1uLAP <= performance comparison between JSON.stringify and fast-deep-equalCupric
The stage 2 Record and Tuple proposal will simplify this dramatically: #{ x: 5, y: 7 } === #{ x: 5, y: 7 }, #[ 4, #{ a: "hello", b: "world" }, 6, 10 ] === #[ 4, #{ b: "world", a: "hello" }, 6, 10 ].Tephrite
This is a concept called shallow Equality Comparision. It will solve this problem https://mcmap.net/q/36980/-how-can-i-determine-equality-for-two-javascript-objectsJudaism
I give really similar nice answer here: https://mcmap.net/q/36579/-generic-deep-diff-between-two-objectsFormulary
@Redblooded why is that wrong? Javascript is full of JSON like objects. If anything it should be very right to compare them.Tactics
@Tactics It isn't "wrong" to deep-compare objects, but it does hint that your code is overly complex, or that you have taken the wrong path to complete what you need to do. There is nearly always more than one way to get things done when writing software, and the simplest approach should always be taken. For example, simply giving your objects a unique id would be simpler, would be more accurate, and would result in a lot less computational power to get the same job done. So "wrong" may be too harsh, and "probably incorrect" is a better way to say this isn't "ideal".Redblooded
@th317erd: In my case, I'm coding xunit-style test assertions. Specifically, assertEqual. This answers true iff the first two arguments are equal -- value, not identify, comparison. I'd like to do as little dispatch myself as possible, so I seek a built-in solution. The order of keys don't matter -- two hashes are equal if they have the same keys and if the value bound to each key has an equal value. It's probably ok to do an identity comparison on each value binding.Guffaw
A
226

The short answer

The simple answer is: No, there is no generic means to determine that an object is equal to another in the sense you mean. The exception is when you are strictly thinking of an object being typeless.

The long answer

The concept is that of an Equals method that compares two different instances of an object to indicate whether they are equal at a value level. However, it is up to the specific type to define how an Equals method should be implemented. An iterative comparison of attributes that have primitive values may not be enough: an object may contain attributes which are not relevant to equality. For example,

 function MyClass(a, b)
 {
     var c;
     this.getCLazy = function() {
         if (c === undefined) c = a * b // imagine * is really expensive
         return c;
     }
  }

In this above case, c is not really important to determine whether any two instances of MyClass are equal, only a and b are important. In some cases c might vary between instances and yet not be significant during comparison.

Note this issue applies when members may themselves also be instances of a type and these each would all be required to have a means of determining equality.

Further complicating things is that in JavaScript the distinction between data and method is blurred.

An object may reference a method that is to be called as an event handler, and this would likely not be considered part of its 'value state'. Whereas another object may well be assigned a function that performs an important calculation and thereby makes this instance different from others simply because it references a different function.

What about an object that has one of its existing prototype methods overridden by another function? Could it still be considered equal to another instance that it otherwise identical? That question can only be answered in each specific case for each type.

As stated earlier, the exception would be a strictly typeless object. In which case the only sensible choice is an iterative and recursive comparison of each member. Even then one has to ask what is the 'value' of a function?

Affettuoso answered 14/10, 2008 at 14:48 Comment(11)
If you're using underscore, you can just do _.isEqual(obj1, obj2);Burghley
@Harsh, the answer failed to give any solution because there is none. Even in Java, there is no silver bullet to object equality comparison and to correctly implement the .equals method is not trivial, which is why there is such a topic dedicated in Effective Java.Parkerparkhurst
@Kumar Harsh, What makes two objects equal is very application specific; not every property of an object should necessarily be taken into consideration, so brute-forcing every property of an object is not a concrete solution either.Poyang
googled javascript equality object, got tl;dr reply, took one-liner from @Burghley comment. thank youBagatelle
If using angular, you have angular.equalsSpeculate
In Node.js, you can use its native assert.deepEqual, see my answer below #201683Godhood
Don't know how this answer is helping any dev trying to find how to solve this problem rather than know why this is a problem. Its like same analogy as to if my laptop is getting frequent BSOD, should I learn the HAL or the kernel to figure out why?Cosmogony
i think you should try with https://mcmap.net/q/36980/-how-can-i-determine-equality-for-two-javascript-objectsLung
If the attempt is compare address in memory to see if 2 handles point to the same "thing" , if have 2 idential copies of an object in 2 different memory addresses without any tracking, something is really wrong...Theran
What is underscore? Is it a library? What is the minimum size code snippet for checking object equality?Wireman
@AaronFranke yes underscore is a utility library similar to lodash. I think for this use case lodash is better because it allows comparing deeply-nested objects, and is known to be more performant. See here for a comparison: geeksforgeeks.org/difference-between-lodash-and-underscorePaean
K
709

Why reinvent the wheel? Give Lodash a try. It has a number of must-have functions such as isEqual().

_.isEqual(object, other);

It will brute force check each key value - just like the other examples on this page - using ECMAScript 5 and native optimizations if they're available in the browser.

Note: Previously this answer recommended Underscore.js, but lodash has done a better job of getting bugs fixed and addressing issues with consistency.

Kimberlykimberlyn answered 7/7, 2010 at 19:35 Comment(14)
Underscore's isEqual function is very nice (but you do have to pull in their library to use it - about 3K gzipped).Barbe
Even if you can't afford to have underscore as a dependency, pull the isEqual function out, satisfy the license requirements and move on. It's by far the most comprehensive equality test mentioned on stackoverflow.Papotto
@DaleAnderson Haha, and now here I am again, looking at the same thing not because I can't use Underscore, but because I actually don't want to, and indeed wanted to look at something like its isEqual for "inspiration".Ament
Just discussing this at work and the problem with _ is that _.isEqual(0,-0) === false and _.isEqual(NaN, NaN) === true -- which violates least surprise, at least for me.Ljoka
There's a fork of Underscore called LoDash and that author is very concerned with consistency issues such as that. Test with LoDash and see what you get.Kimberlykimberlyn
Underscore.js' implementation of isEqual is failing for me on two objects that have the exact same properties (as seen in Firebug and tested with JSON.stringify) and look like this: {"from":70,"to":[1283,1301],"subject":"test","body":"test","bodyType":"text","priority":"high"}Beguine
@Barbe you can use the standalone module if you don't want the entire library npmjs.com/package/lodash.isequalCaerphilly
If you use webpack and ES6, you can just import the lodash function that you want to use. e.g. import { isEqual } from 'lodash'. Then your bundled JS will only include that function, and skip everything that you're not using.Forrestforrester
Also consider the deep-equal package, which even works for arrays of objects.Osmund
How is saying "oh, just use X package!" helpful? You're telling the person to abstract and bulk their code with no real explanation of how to actually get to a solution themselves. I'm not saying don't use packages or suggest them but the JavaScript ecosystem is fickle, you should promote an actual understanding of a solution, not just a way to temporarily get around it.Seismograph
Install lodash with Angular 4 only to get rid of itBazluke
As this was last edited 6 years ago, but still is the top answer I have to ask: is using external libraries still the proper way in 2022?Akira
@toddmo Avoid moment and the like at all costs. All you need to compare dates is new Date(milliseconds).valueOf() === new Date(milliseconds).valueOf(). If you need to consider timezones, consider xtz.js, which uses the native timezone functions.Kimberlykimberlyn
@toddmo Well, exactly what it says on the homepage for one: "Considering using Moment in your project? There may be better modern alternatives. For more details and recommendations, please see Project Status in the docs." It's bloated, slow, and deprecated.Kimberlykimberlyn
A
226

The short answer

The simple answer is: No, there is no generic means to determine that an object is equal to another in the sense you mean. The exception is when you are strictly thinking of an object being typeless.

The long answer

The concept is that of an Equals method that compares two different instances of an object to indicate whether they are equal at a value level. However, it is up to the specific type to define how an Equals method should be implemented. An iterative comparison of attributes that have primitive values may not be enough: an object may contain attributes which are not relevant to equality. For example,

 function MyClass(a, b)
 {
     var c;
     this.getCLazy = function() {
         if (c === undefined) c = a * b // imagine * is really expensive
         return c;
     }
  }

In this above case, c is not really important to determine whether any two instances of MyClass are equal, only a and b are important. In some cases c might vary between instances and yet not be significant during comparison.

Note this issue applies when members may themselves also be instances of a type and these each would all be required to have a means of determining equality.

Further complicating things is that in JavaScript the distinction between data and method is blurred.

An object may reference a method that is to be called as an event handler, and this would likely not be considered part of its 'value state'. Whereas another object may well be assigned a function that performs an important calculation and thereby makes this instance different from others simply because it references a different function.

What about an object that has one of its existing prototype methods overridden by another function? Could it still be considered equal to another instance that it otherwise identical? That question can only be answered in each specific case for each type.

As stated earlier, the exception would be a strictly typeless object. In which case the only sensible choice is an iterative and recursive comparison of each member. Even then one has to ask what is the 'value' of a function?

Affettuoso answered 14/10, 2008 at 14:48 Comment(11)
If you're using underscore, you can just do _.isEqual(obj1, obj2);Burghley
@Harsh, the answer failed to give any solution because there is none. Even in Java, there is no silver bullet to object equality comparison and to correctly implement the .equals method is not trivial, which is why there is such a topic dedicated in Effective Java.Parkerparkhurst
@Kumar Harsh, What makes two objects equal is very application specific; not every property of an object should necessarily be taken into consideration, so brute-forcing every property of an object is not a concrete solution either.Poyang
googled javascript equality object, got tl;dr reply, took one-liner from @Burghley comment. thank youBagatelle
If using angular, you have angular.equalsSpeculate
In Node.js, you can use its native assert.deepEqual, see my answer below #201683Godhood
Don't know how this answer is helping any dev trying to find how to solve this problem rather than know why this is a problem. Its like same analogy as to if my laptop is getting frequent BSOD, should I learn the HAL or the kernel to figure out why?Cosmogony
i think you should try with https://mcmap.net/q/36980/-how-can-i-determine-equality-for-two-javascript-objectsLung
If the attempt is compare address in memory to see if 2 handles point to the same "thing" , if have 2 idential copies of an object in 2 different memory addresses without any tracking, something is really wrong...Theran
What is underscore? Is it a library? What is the minimum size code snippet for checking object equality?Wireman
@AaronFranke yes underscore is a utility library similar to lodash. I think for this use case lodash is better because it allows comparing deeply-nested objects, and is known to be more performant. See here for a comparison: geeksforgeeks.org/difference-between-lodash-and-underscorePaean
N
202

The default equality operator in JavaScript for Objects yields true when they refer to the same location in memory.

var x = {};
var y = {};
var z = x;

x === y; // => false
x === z; // => true

If you require a different equality operator you'll need to add an equals(other) method, or something like it to your classes and the specifics of your problem domain will determine what exactly that means.

Here's a playing card example:

function Card(rank, suit) {
  this.rank = rank;
  this.suit = suit;
  this.equals = function(other) {
     return other.rank == this.rank && other.suit == this.suit;
  };
}

var queenOfClubs = new Card(12, "C");
var kingOfSpades = new Card(13, "S");

queenOfClubs.equals(kingOfSpades); // => false
kingOfSpades.equals(new Card(13, "S")); // => true
Nocturne answered 20/5, 2009 at 3:48 Comment(6)
If the object(s) can be converted to a JSON string, then it makes an equals() function simple.Bergius
@Bergius Not always. Converting objects to JSON and comparing strings can become computationally intensive for complex objects in tight loops. For simple objects it probably doesn't matter much, but in reality it truly depends on your specific situation. A correct solution may be as simple as comparing object IDs or checking each property, but its correctness is dictated entirely by the problem domain.Nocturne
Shouldn't we compare the data type as well?! return other.rank === this.rank && other.suit === this.suit;Pelt
@Pelt probably not. In JavaScript types are pretty fast and loose, but if in your domain types are important then you may want to check types as well.Nocturne
@Bergius Other problem with converting to JSON is that the order of the properties in the string becomes significant. {x:1, y:2} !== {y:2, x:1}Propagate
JavaScript, of course, has no classes. That's a newable. ES6 classes are simply newable sugar. Otherwise, this is probably the correct answer.Jolda
G
114

Short functional deepEqual implementation:

function deepEqual(x, y) {
  return (x && y && typeof x === 'object' && typeof y === 'object') ?
    (Object.keys(x).length === Object.keys(y).length) &&
      Object.keys(x).reduce(function(isEqual, key) {
        return isEqual && deepEqual(x[key], y[key]);
      }, true) : (x === y);
}

Edit: version 2, using jib's suggestion and ES6 arrow functions:

function deepEqual(x, y) {
  const ok = Object.keys, tx = typeof x, ty = typeof y;
  return x && y && tx === 'object' && tx === ty ? (
    ok(x).length === ok(y).length &&
      ok(x).every(key => deepEqual(x[key], y[key]))
  ) : (x === y);
}
Grandee answered 3/10, 2015 at 11:36 Comment(16)
You could replace reduce with every to simplify.Gine
@Gine no he couldn't.Newark
@Newark sure he could: Object.keys(x).every(key => deepEqual(x[key], y[key])).Gine
@Gine You're right, it's simplified without the previousValue arguement of the callback function.Newark
This fails when you are comparing two datesWindmill
True. Or regexes, or class instances (typeof x === 'function'). To overcome this, check x === y can be replaced with String(x) === String(y).Grandee
deepEqual({}, []) returns trueDoubloon
yes, if you care for such corner case, ugly solution is to replace : (x === y) with : (x === y && (x != null && y != null || x.constructor === y.constructor))Grandee
I like this answer, but needs some changes to be better.Euphemism
Even when replacing (x === y) with (x === y && (x != null && y != null || x.constructor === y.constructor)), the function(v2) still returns true(in NodeJS) when comparing {} and [].Coffeehouse
: (x === y) is not the place to try to fix the {} vs [] comparison. Instead put && a.constructor === b.constructor at the end of the main condition (i.e. before the ?).Danielladanielle
Does not work if the order of the object keys has changed.Desuetude
Yes it does @IsaacPak , you probably commented on the wrong answer :P The function is order agnostic as far as I'm concerned at least. Made a jest test and works with all the possible variations. gist.github.com/exapsy/762064fa04ac613f4976ab4c59492f39Crotchety
Hi there, I wanted to get clarification on the : x === y part... Isn't this establishing equality? return x && y && tx === 'object' && tx === ty I'm struggling to understand the logic of : x === y ...Auto
x && y && tx === 'object' && tx === ty asks "should we treat x and y as objects?". If yes, "then" part of the ternary checks length of keys are equal and recursively calls for each key. If no, "else" part of the ternary, x === y, simply performs strict equality test.Grandee
Fails if the objects contain identical arrays with elements in different order.Satanic
F
88

This is my version. It is using new Object.keys feature that is introduced in ES5 and ideas/tests from +, + and +:

function objectEquals(x, y) {
    'use strict';

    if (x === null || x === undefined || y === null || y === undefined) { return x === y; }
    // after this just checking type of one would be enough
    if (x.constructor !== y.constructor) { return false; }
    // if they are functions, they should exactly refer to same one (because of closures)
    if (x instanceof Function) { return x === y; }
    // if they are regexps, they should exactly refer to same one (it is hard to better equality check on current ES)
    if (x instanceof RegExp) { return x === y; }
    if (x === y || x.valueOf() === y.valueOf()) { return true; }
    if (Array.isArray(x) && x.length !== y.length) { return false; }

    // if they are dates, they must had equal valueOf
    if (x instanceof Date) { return false; }

    // if they are strictly equal, they both need to be object at least
    if (!(x instanceof Object)) { return false; }
    if (!(y instanceof Object)) { return false; }

    // recursive object equality check
    var p = Object.keys(x);
    return Object.keys(y).every(function (i) { return p.indexOf(i) !== -1; }) &&
        p.every(function (i) { return objectEquals(x[i], y[i]); });
}


///////////////////////////////////////////////////////////////
/// The borrowed tests, run them by clicking "Run code snippet"
///////////////////////////////////////////////////////////////
var printResult = function (x) {
    if (x) { document.write('<div style="color: green;">Passed</div>'); }
    else { document.write('<div style="color: red;">Failed</div>'); }
};
var assert = { isTrue: function (x) { printResult(x); }, isFalse: function (x) { printResult(!x); } }
assert.isTrue(objectEquals(null,null));
assert.isFalse(objectEquals(null,undefined));
assert.isFalse(objectEquals(/abc/, /abc/));
assert.isFalse(objectEquals(/abc/, /123/));
var r = /abc/;
assert.isTrue(objectEquals(r, r));

assert.isTrue(objectEquals("hi","hi"));
assert.isTrue(objectEquals(5,5));
assert.isFalse(objectEquals(5,10));

assert.isTrue(objectEquals([],[]));
assert.isTrue(objectEquals([1,2],[1,2]));
assert.isFalse(objectEquals([1,2],[2,1]));
assert.isFalse(objectEquals([1,2],[1,2,3]));

assert.isTrue(objectEquals({},{}));
assert.isTrue(objectEquals({a:1,b:2},{a:1,b:2}));
assert.isTrue(objectEquals({a:1,b:2},{b:2,a:1}));
assert.isFalse(objectEquals({a:1,b:2},{a:1,b:3}));

assert.isTrue(objectEquals({1:{name:"mhc",age:28}, 2:{name:"arb",age:26}},{1:{name:"mhc",age:28}, 2:{name:"arb",age:26}}));
assert.isFalse(objectEquals({1:{name:"mhc",age:28}, 2:{name:"arb",age:26}},{1:{name:"mhc",age:28}, 2:{name:"arb",age:27}}));

Object.prototype.equals = function (obj) { return objectEquals(this, obj); };
var assertFalse = assert.isFalse,
    assertTrue = assert.isTrue;

assertFalse({}.equals(null));
assertFalse({}.equals(undefined));

assertTrue("hi".equals("hi"));
assertTrue(new Number(5).equals(5));
assertFalse(new Number(5).equals(10));
assertFalse(new Number(1).equals("1"));

assertTrue([].equals([]));
assertTrue([1,2].equals([1,2]));
assertFalse([1,2].equals([2,1]));
assertFalse([1,2].equals([1,2,3]));
assertTrue(new Date("2011-03-31").equals(new Date("2011-03-31")));
assertFalse(new Date("2011-03-31").equals(new Date("1970-01-01")));

assertTrue({}.equals({}));
assertTrue({a:1,b:2}.equals({a:1,b:2}));
assertTrue({a:1,b:2}.equals({b:2,a:1}));
assertFalse({a:1,b:2}.equals({a:1,b:3}));

assertTrue({1:{name:"mhc",age:28}, 2:{name:"arb",age:26}}.equals({1:{name:"mhc",age:28}, 2:{name:"arb",age:26}}));
assertFalse({1:{name:"mhc",age:28}, 2:{name:"arb",age:26}}.equals({1:{name:"mhc",age:28}, 2:{name:"arb",age:27}}));

var a = {a: 'text', b:[0,1]};
var b = {a: 'text', b:[0,1]};
var c = {a: 'text', b: 0};
var d = {a: 'text', b: false};
var e = {a: 'text', b:[1,0]};
var i = {
    a: 'text',
    c: {
        b: [1, 0]
    }
};
var j = {
    a: 'text',
    c: {
        b: [1, 0]
    }
};
var k = {a: 'text', b: null};
var l = {a: 'text', b: undefined};

assertTrue(a.equals(b));
assertFalse(a.equals(c));
assertFalse(c.equals(d));
assertFalse(a.equals(e));
assertTrue(i.equals(j));
assertFalse(d.equals(k));
assertFalse(k.equals(l));

// from comments on stackoverflow post
assert.isFalse(objectEquals([1, 2, undefined], [1, 2]));
assert.isFalse(objectEquals([1, 2, 3], { 0: 1, 1: 2, 2: 3 }));
assert.isFalse(objectEquals(new Date(1234), 1234));

// no two different function is equal really, they capture their context variables
// so even if they have same toString(), they won't have same functionality
var func = function (x) { return true; };
var func2 = function (x) { return true; };
assert.isTrue(objectEquals(func, func));
assert.isFalse(objectEquals(func, func2));
assert.isTrue(objectEquals({ a: { b: func } }, { a: { b: func } }));
assert.isFalse(objectEquals({ a: { b: func } }, { a: { b: func2 } }));
Foiled answered 28/5, 2013 at 9:28 Comment(14)
objectEquals([1,2,undefined],[1,2]) returns trueTimer
objectEquals([1,2,3],{0:1,1:2,2:3}) also returns true -- e.g. there is no type checking, only key/value checking.Timer
objectEquals(new Date(1234),1234) returns trueTimer
objectEquals(/1234/,/asdf/) returns trueTimer
var x={},y={};x.a=x;y.a=y; objectEquals(x,y) throws Uncaught RangeError: Maximum call stack size exceeded -- i.e. fails under parallel circular references.Timer
Thank you. I've fixed the regexp issue but not sure what can be done with circular references.Foiled
Execution falls past valueOf check if not equal... which could be a problem for alternate Number or String classes -- or really, any custom object or class that overrides valueOf.Timer
I've never seen alternate Number String classes. Java for example, something like String is marked as final to prevent abuses 1.Foiled
Here's one: mikemcl.github.io/bignumber.js. Its implementation of valueOf outputs a string representation of the number. (Note: in the case of this library, your function is not broken since properties defining the value are accessible independently of the value returned from valueOf.)Timer
if (x.constructor !== y.constructor) { return false; } This would break when comparing two 'new String('a')' in different windows. For value equality, you'd have to check whether String.isString on both objects, then use a loose equality check 'a == b'.Tendon
There's a huge difference between "value" equality and "strict" equality and they shouldn't be implemented the same way. Value equality shouldn't care about types, aside from the basic structure, which is one of these 4: 'object' (i.e. a collection of key/value pairs), 'number', 'string', or 'array'. That's it. Anything that's not a number, string, or array, should be compared as a set of key/value pairs, regardless of what the constructor is (cross-window-safe). When comparing objects, equate the value of literal numbers and instances of Number, but don't coerce strings to numbers.Tendon
A function is an exceptional type that's not really a "value", aside from it being a pointer to some logic, so if it's to be compared at all, it should be compared for strict instance equality with another function (i.e. they're the exact same function).Tendon
A huge problem with cross-window comparisons, is that functions like String.isString(a) will tell you something is a string, but if you subsequently call a.equals where you've defined equals on the string class, it may be undefined if 'a' came from another where it's a string (literal or instance of String), but does not have 'equals' defined.Tendon
false positive with new Map([[1,1]]) new Map() and false negative with {constructor: NaN} {constructor: NaN}Resentful
E
87

If you are working in AngularJS, the angular.equals function will determine if two objects are equal. In Ember.js use isEqual.

  • angular.equals - See the docs or source for more on this method. It does a deep compare on arrays too.
  • Ember.js isEqual - See the docs or source for more on this method. It does not do a deep compare on arrays.

var purple = [{"purple": "drank"}];
var drank = [{"purple": "drank"}];

if(angular.equals(purple, drank)) {
    document.write('got dat');
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.5/angular.min.js"></script>
Euphrates answered 27/9, 2013 at 16:13 Comment(0)
G
71

If you are using a JSON library, you can encode each object as JSON, then compare the resulting strings for equality.

var obj1={test:"value"};
var obj2={test:"value2"};

alert(JSON.encode(obj1)===JSON.encode(obj2));

NOTE: While this answer will work in many cases, as several people have pointed out in the comments it's problematic for a variety of reasons. In pretty much all cases you'll want to find a more robust solution.

Garey answered 14/10, 2008 at 14:11 Comment(12)
Interesting, but a little tricky in my opinion. For example, can you 100% guarantee that the object properties will be generated always in the same order ?Retrorse
That's a good question, and raises another, as to whether two objects with the same properties in different orders are really equal or not. Depends upon what you mean by equal, I guess.Garey
This is definitely one of the more interesting answers, and is some good info -- thanks for passing it along!Pariah
Note that most encoders and stringifiers ignore functions and convert nonfinite numbers, like NaN, to null.Jaymie
I agree with Guido, order of properties are important and it cannot be guaranteed. @JoelAnair, I think two objects with the same properties in different orders should be considered equal if the value of the properties are equal.Styracaceous
You may be able to still use JSON stringification, if you stringify obj1 against a mixin of obj2 into obj2 (deep copy). I've posted a working demo of this as an answer.Kosiur
@JoelAnair, properties have no order.Prang
@GuidoGarcía @Juzer JSON.stringify does not guarantee key order. Mozilla docs say "Properties of non-array objects are not guaranteed to be stringified in any particular order. Do not rely on ordering of properties within the same object within the stringification."Fishbolt
This could work with an alternate JSON stringifier, one that sorts object keys consistently.Timer
That is tricky, since you may add some property in text input, then clear it and it will remain with someProperty: "" and this will make them not even - obj a = {} obj b = {}, then obj a = {someProperty: ""}Mattock
Property 'encode' does not exist on type 'JSON' on Javascript.Dyanne
Note that this answer was from 2008, when JSON wasn't part of the JS spec yet. With the modern JSON.stringy and JSON.parse functions, this solution works fine, as we can ensure keys are always serialized in the same order by passing a preprocessing function to stringify (that second argument that you never use. It is incredibly powerful).Ferule
D
41

For those of you using Node, there is a convenient method called isDeepStrictEqual on the native util library that can achieve this.

const util = require('util');

const obj1 = {
  foo: "bar",
  baz: [1, 2]
};

const obj2 = {
  foo: "bar",
  baz: [1, 2]
};


obj1 == obj2 // false
util.isDeepStrictEqual(obj1, obj2) // true

https://nodejs.org/api/util.html#util_util_isdeepstrictequal_val1_val2

Deedee answered 22/1, 2019 at 17:31 Comment(2)
its performance is suppose to be good. No worries. Even i use this in complex scenarios as well. As when we use this we don't have to worry if property of object is bearing Object or array. Json.Stringify makes it string anyway and comparison of strings in javascript is not a big dealTinct
Thank you for this answer! It's added in Node v9Scherzando
G
30

In Node.js, you can use its native require("assert").deepStrictEqual. More info: http://nodejs.org/api/assert.html

For example:

var assert = require("assert");
assert.deepStrictEqual({a:1, b:2}, {a:1, b:3}); // will throw AssertionError

Another example that returns true / false instead of returning errors:

var assert = require("assert");

function deepEqual(a, b) {
    try {
      assert.deepEqual(a, b);
    } catch (error) {
      if (error.name === "AssertionError") {
        return false;
      }
      throw error;
    }
    return true;
};
Godhood answered 24/9, 2014 at 12:26 Comment(3)
Chai has this feature too. In its case, you'll use: var foo = { a: 1 }; var bar = { a: 1 }; expect(foo).to.deep.equal(bar); // true;Disrobe
Some versions of Node.js set error.name to "AssertionError [ERR_ASSERTION]". In this case, I'd replace the if statement with if (error.code === 'ERR_ASSERTION') {.Tonne
I had no idea deepStrictEqual was the way to go. I'd been wracking my brain trying to figure out why strictEqual wasn't working. Fantastic.Paraplegia
C
28

This question has more than 30 answers already. I am going to summarize and explain them (with a "my father" analogy) and add my suggested solution.

You have 4+1 classes of solutions:


1) Use a hacky incomplete quick one-liner

Good if you are in a rush and 99% correctness works.

Examples of this is, JSON.stringify() suggested by Pratik Bhalodiya, or JSON.encode by Joel Anair, or .toString(), or other methods that transform your objects into a String and then compare the two Strings using === character by character.

The drawback, however, is that there is no globally standard unique representation of an Object in String. e.g. { a: 5, b: 8} and {b: 8 and a: 5 } are equal.

  • Pros: Fast, quick.
  • Cons: Hopefully works! It will not work if the environment/browser/engine memorizes the ordering for objects (e.g. Chrome/V8) and the order of the keys are different (Thanks to Eksapsy.) So, not guaranteed at all. Performance wouldn't be great either in large objects.

My Father Analogy

When I am talking about my father, "my tall handsome father" and "my handsome tall father" are the same person! But the two strings are not the same.

Note that there is actually a correct (standard way) order of adjectives in English grammar, which says it should be a "handsome tall man," but you are risking your competency if you blindly assume Javascript engine of iOS 8 Safari is also abiding the same grammar, blindly! #WelcomeToJavascriptNonStandards


2) Write your own DIY recursive function

Good if you are learning.

Examples are atmin's solution.

The biggest disadvantage is you will definitely miss some edge cases. Have you considered a self-reference in object values? Have you considered NaN? Have you considered two objects that have the same ownProperties but different prototypical parents?

I would only encourage people to do this if they are practicing and the code is not going to go in production. That's the only case that reinventing the wheel has justifications.

  • Pros: Learning opportunity.
  • Cons: Not reliable. Takes time and concerns.

My Father Analogy

It's like assuming if my dad's name is "John Smith" and his birthday is "1/1/1970", then anyone whose name is "John Smith" and is born on "1/1/1970" is my father.

That's usually the case, but what if there are two "John Smith"s born on that day? If you think you will consider their height, then that's increasing the accuracy but still not a perfect comparison.

2.1 You limited scope DIY comparator

Rather than going on a wild chase of checking all the properties recursively, one might consider checking only "a limited" number of properties. For instance, if the objects are Users, you can compare their emailAddress field.

It's still not a perfect one, but the benefits over solution #2 are:

  1. It's predictable, and it's less likely to crash.
  2. You are driving the "definition" of equality, rather than relying on a wild form and shape of the Object and its prototype and nested properties.

3) Use a library version of equal function

Good if you need a production-level quality, and you cannot change the design of the system.

Examples are _.equal of lodash, already in coolaj86's answer or Angular's or Ember's as mentioned in Tony Harvey's answer or Node's by Rafael Xavier.

  • Pros: It's what everyone else does.
  • Cons: External dependency, which can cost you extra memory/CPU/Security concerns, even a little bit. Also, can still miss some edge cases (e.g. whether two objects having same ownProperties but different prototypical parents should be considered the same or not.) Finally, you might be unintentionally band-aiding an underlying design problem with this; just saying!

My Father Analogy

It's like paying an agency to find my biological father, based on his phone, name, address, etc.

It's gonna cost more, and it's probably more accurate than myself running the background check, but doesn't cover edge cases like when my father is immigrant/asylum and his birthday is unknown!


4) Use an IDentifier in the Object

Good if you [still] can change the design of the system (objects you are dealing with) and you want your code to last long.

It's not applicable in all cases, and might not be very performant. However, it's a very reliable solution, if you can make it.

The solution is, every object in the system will have a unique identifier along with all the other properties. The uniqueness of the identifier will be guaranteed at the time of generation. And you will use this ID (also known as UUID/GUID -- Globally/Universally Unique Identifier) when it comes to comparing two objects. i.e. They are equal if and only if these IDs are equal.

The IDs can be simple auto_incremental numbers, or a string generated via a library (advised) or a piece of code. All you need to do is make sure it's always unique, which in case of auto_incremental it can be built-in, or in case of UUID, can be checked will all existing values (e.g. MySQL's UNIQUE column attribute) or simply (if coming from a library) be relied upon giving the extremely low likelihood of a collision.

Note that you also need to store the ID with the object at all times (to guarantee its uniqueness), and computing it in real-time might not be the best approach.

  • Pros: Reliable, efficient, not dirty, modern.
  • Cons: Needs extra space. Might need a redesign of the system.

My Father Analogy

It's like known my father's Social Security Number is 911-345-9283, so anyone who has this SSN is my father, and anyone who claims to be my father must have this SSN.


Conclusion

I personally prefer solution #4 (ID) over them all for accuracy and reliability. If it's not possible I'd go with #2.1 for predictability, and then #3. If neither is possible, #2 and finally #1.

Cello answered 19/8, 2020 at 22:59 Comment(4)
The first "hacky" solution also doesn't work at all when the order of the objects is different. eg. o1 = { a: '1', b: '2' } - o2 = { b: '2', a: '1' } compare JSON.stringify(o1) === JSON.stringify(o2) = falseCrotchety
Your preferred method assumes that the object has a specific use, such as being a dataset for a unique object, as opposed to another use, such as being a set of items with repetition where the key/property is the item and the value is the number of times the item is in the set (which would require every property and value to be checked).Mistranslate
@DaveF in that case, perhaps Map would be more appropriate than a wild object, semantically.Cello
@Cello My point was that sometimes all properties and values need to be checked. The example is not important.Mistranslate
D
27

Are you trying to test if two objects are the equal? ie: their properties are equal?

If this is the case, you'll probably have noticed this situation:

var a = { foo : "bar" };
var b = { foo : "bar" };
alert (a == b ? "Equal" : "Not equal");
// "Not equal"

you might have to do something like this:

function objectEquals(obj1, obj2) {
    for (var i in obj1) {
        if (obj1.hasOwnProperty(i)) {
            if (!obj2.hasOwnProperty(i)) return false;
            if (obj1[i] != obj2[i]) return false;
        }
    }
    for (var i in obj2) {
        if (obj2.hasOwnProperty(i)) {
            if (!obj1.hasOwnProperty(i)) return false;
            if (obj1[i] != obj2[i]) return false;
        }
    }
    return true;
}

Obviously that function could do with quite a bit of optimisation, and the ability to do deep checking (to handle nested objects: var a = { foo : { fu : "bar" } }) but you get the idea.

As FOR pointed out, you might have to adapt this for your own purposes, eg: different classes may have different definitions of "equal". If you're just working with plain objects, the above may suffice, otherwise a custom MyClass.equals() function may be the way to go.

Delgadillo answered 14/10, 2008 at 13:59 Comment(2)
It is a long method but it completely tests the objects without making any assumptions on the order of the properties in each object.Absorbefacient
does not work if a property is an array of other objectsErased
K
24

If you have a deep copy function handy, you can use the following trick to still use JSON.stringify while matching the order of properties:

function equals(obj1, obj2) {
    function _equals(obj1, obj2) {
        return JSON.stringify(obj1)
            === JSON.stringify($.extend(true, {}, obj1, obj2));
    }
    return _equals(obj1, obj2) && _equals(obj2, obj1);
}

Demo: http://jsfiddle.net/CU3vb/3/

Rationale:

Since the properties of obj1 are copied to the clone one by one, their order in the clone will be preserved. And when the properties of obj2 are copied to the clone, since properties already existing in obj1 will simply be overwritten, their orders in the clone will be preserved.

Kosiur answered 14/6, 2012 at 19:20 Comment(7)
I don't think order preservation is guaranteed across browsers/engines.Argonaut
@JoLiss Citations needed ;) I recall testing this in multiple browsers, getting consistent results. But of course, no one can guarantee the behaviour remaining the same in future browsers/engines. This is a trick (as already called out in the answer) at best, and I didn't mean it to be a surefire way to compare objects.Kosiur
Sure, here's some pointers: ECMAScript spec says object is "unordered"; and this answer for actual diverging behavior on current browsers.Argonaut
@JoLiss Thanks for that! But please note I was never claiming the preservation of order between code and compiled object. I was claiming preservation of order of properties whose values get replaced in-place. That was the key with my solution: to use a mixin to just overwrite property values. Assuming implementations generally opt to use some sort of hashmap, replacing just values should preserve the order of keys. It is in fact exactly this that I had tested in different browsers.Kosiur
Oh of course, now I see what you mean. Hm. This is still obscure enough (and under-specified enough) to make me queasy, but I can see that it might actually be consistent. It's definitely an interesting hack.Argonaut
@AtesGoral: is it possible to make this constraint a bit more explicit (boldface,...). Most people simply do copy-paste without reading the text around it...Hellespont
function nesting is not a good for performance when it's called a lot which in this case could/would do that.Nymphalid
L
24

Simplest and logical solutions for comparing everything Like Object, Array, String, Int...

JSON.stringify({a: val1}) === JSON.stringify({a: val2})

Note:

  • you need to replace val1and val2 with your Object
  • for the object, you have to sort(by key) recursively for both side objects
Lung answered 9/10, 2017 at 11:35 Comment(3)
I am assuming that this won't work in many cases because the order of keys in objects doesn't matter - unless JSON.stringify does an alphabetical reordering? (Which I cannot find documented.)Eternal
yup you are right ... for the object, you have to sort recursively for both side objectsLung
This does not work for objects with circular referencesOlomouc
D
22
var object1 = {name: "humza" , gender : "male", age: 23}
var object2 = {name: "humza" , gender : "male", age: 23}
var result = Object.keys(object1).every((key) =>  object1[key] === object2[key])

Result will be true if object1 has same values on object2.

Devol answered 10/4, 2020 at 14:47 Comment(3)
This won't work if object2 has additional keys which object1 doesn't include.Expediential
as @Ram Kumar mentioned this will work only if you loop both objects, not very efficient but for small objects I think it will be faster the stringifying the objects - not 100% sure thoughCinquecento
@RamKumar just add in the condition var result = Object.keys(object1).every((key) =>(object1[key] === object2[key] && object1.hasOwnProperty(key) && object2.hasOwnProperty(key))Straightway
G
16

I use this comparable function to produce copies of my objects that are JSON comparable:

var comparable = o => (typeof o != 'object' || !o)? o :
  Object.keys(o).sort().reduce((c, key) => (c[key] = comparable(o[key]), c), {});

// Demo:

var a = { a: 1, c: 4, b: [2, 3], d: { e: '5', f: null } };
var b = { b: [2, 3], c: 4, d: { f: null, e: '5' }, a: 1 };

console.log(JSON.stringify(comparable(a)));
console.log(JSON.stringify(comparable(b)));
console.log(JSON.stringify(comparable(a)) == JSON.stringify(comparable(b)));
<div id="div"></div>

Comes in handy in tests (most test frameworks have an is function). E.g.

is(JSON.stringify(comparable(x)), JSON.stringify(comparable(y)), 'x must match y');

If a difference is caught, strings get logged, making differences spottable:

x must match y
got      {"a":1,"b":{"0":2,"1":3},"c":7,"d":{"e":"5","f":null}},
expected {"a":1,"b":{"0":2,"1":3},"c":4,"d":{"e":"5","f":null}}.
Gine answered 6/3, 2016 at 1:4 Comment(1)
good idea (in my case the objects to be compared a just key/value-pairs, no Special things)Napoleonnapoleonic
C
13

Heres's a solution in ES6/ES2015 using a functional-style approach:

const typeOf = x => 
  ({}).toString
      .call(x)
      .match(/\[object (\w+)\]/)[1]

function areSimilar(a, b) {
  const everyKey = f => Object.keys(a).every(f)

  switch(typeOf(a)) {
    case 'Array':
      return a.length === b.length &&
        everyKey(k => areSimilar(a.sort()[k], b.sort()[k]));
    case 'Object':
      return Object.keys(a).length === Object.keys(b).length &&
        everyKey(k => areSimilar(a[k], b[k]));
    default:
      return a === b;
  }
}

demo available here

Cabe answered 25/8, 2015 at 5:14 Comment(1)
Does not work if the order of the object keys has changed.Desuetude
R
12

I don't know if anyone's posted anything similar to this, but here's a function I made to check for object equalities.

function objectsAreEqual(a, b) {
  for (var prop in a) {
    if (a.hasOwnProperty(prop)) {
      if (b.hasOwnProperty(prop)) {
        if (typeof a[prop] === 'object') {
          if (!objectsAreEqual(a[prop], b[prop])) return false;
        } else {
          if (a[prop] !== b[prop]) return false;
        }
      } else {
        return false;
      }
    }
  }
  return true;
}

Also, it's recursive, so it can also check for deep equality, if that's what you call it.

Refit answered 5/3, 2017 at 17:13 Comment(2)
small correction : before going through each props in a and b add this check if(Object.getOwnPropertyNames(a).length !== Object.getOwnPropertyNames(b).length ) return falseBethelbethena
it is obvious that proper equality checker must be recursive. I think one of such recursive answers should be the correct answer. The accepted answer does not give code and it does not helpReference
C
8

ES6: The minimum code I could get it done, is this. It do deep comparison recursively by stringifying all key value array sorted representing the object, the only limitation is no methods or symbols are compare.

const compareObjects = (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))
}

console.log(compareObjects({b:4,a:{b:1}}, {a:{b:1},b:4}));

IMPORTANT: This function is doing a JSON.stringfy in an ARRAY with the keys sorted and NOT in the object it self:

  1. ["a", ["b", 1]]
  2. ["b", 4]
Ceballos answered 24/9, 2019 at 16:47 Comment(5)
This is a fully functional answer, thanks @Adriano Spadoni. Do you know how can I get the key/attribute that was modified? Thanks,Sima
hi @digital, if you need which keys are different, this is not the ideal function. Check the other answer and use one with a loop through the objects.Ceballos
Do not ever use JSON.stringify to compare json objects. Order of the keys is not expected to be the same.Crotchety
Hi @Eksapsy, that is why there is a "sort()" function, do you see the s(a) s(b)? this function is fine due to the sort. The example has keys in different other on purpose to prove it works.Ceballos
@digital, to get the difference would need a RegExp instead of the "===", it is doable.Ceballos
A
6

Below is a short implementation which uses JSON.stringify but sorts the keys as @Jor suggested here.

Some tests were taken from the answer of @EbrahimByagowi here.

Of course, by using JSON.stringify, the solution is limited to JSON-serializable types (a string, a number, a JSON object, an array, a boolean, null). Objects like Date, Function, etc. are not supported.

function objectEquals(obj1, obj2) {
  const JSONstringifyOrder = obj => {
    const keys = {};
    JSON.stringify(obj, (key, value) => {
      keys[key] = null;
      return value;
    });
    return JSON.stringify(obj, Object.keys(keys).sort());
  };
  return JSONstringifyOrder(obj1) === JSONstringifyOrder(obj2);
}

///////////////////////////////////////////////////////////////
/// The borrowed tests, run them by clicking "Run code snippet"
///////////////////////////////////////////////////////////////
var printResult = function (x) {
    if (x) { document.write('<div style="color: green;">Passed</div>'); }
    else { document.write('<div style="color: red;">Failed</div>'); }
};
var assert = { isTrue: function (x) { printResult(x); }, isFalse: function (x) { printResult(!x); } }

assert.isTrue(objectEquals("hi","hi"));
assert.isTrue(objectEquals(5,5));
assert.isFalse(objectEquals(5,10));

assert.isTrue(objectEquals([],[]));
assert.isTrue(objectEquals([1,2],[1,2]));
assert.isFalse(objectEquals([1,2],[2,1]));
assert.isFalse(objectEquals([1,2],[1,2,3]));

assert.isTrue(objectEquals({},{}));
assert.isTrue(objectEquals({a:1,b:2},{a:1,b:2}));
assert.isTrue(objectEquals({a:1,b:2},{b:2,a:1}));
assert.isFalse(objectEquals({a:1,b:2},{a:1,b:3}));

assert.isTrue(objectEquals({1:{name:"mhc",age:28}, 2:{name:"arb",age:26}},{1:{name:"mhc",age:28}, 2:{name:"arb",age:26}}));
assert.isFalse(objectEquals({1:{name:"mhc",age:28}, 2:{name:"arb",age:26}},{1:{name:"mhc",age:28}, 2:{name:"arb",age:27}}));
Automation answered 16/4, 2021 at 18:49 Comment(0)
D
5

Just wanted to contribute my version of objects comparison utilizing some es6 features. It doesn't take an order into account. After converting all if/else's to ternary I've came with following:

function areEqual(obj1, obj2) {

    return Object.keys(obj1).every(key => {

            return obj2.hasOwnProperty(key) ?
                typeof obj1[key] === 'object' ?
                    areEqual(obj1[key], obj2[key]) :
                obj1[key] === obj2[key] :
                false;

        }
    )
}
Despain answered 15/11, 2016 at 16:2 Comment(0)
R
4

EDIT: This method is quite flawed, and is rife with its own issues. I don't recommend it, and would appreciate some down-votes! It is problematic because 1) Some things can not be compared (i.e. functions) because they can not be serialized, 2) It isn't a very fast method of comparing, 3) It has ordering issues, 4) It can have collision issues/false positives if not properly implemented, 5) It can't check for "exactness" (===), and instead is based of value equality, which is oftentimes not what is desired in a comparison method.

A simple solution to this issue that many people don't realize is to sort the JSON strings (per character). This is also usually faster than the other solutions mentioned here:

function areEqual(obj1, obj2) {
    var a = JSON.stringify(obj1), b = JSON.stringify(obj2);
    if (!a) a = '';
    if (!b) b = '';
    return (a.split('').sort().join('') == b.split('').sort().join(''));
}

Another useful thing about this method is you can filter comparisons by passing a "replacer" function to the JSON.stringify functions (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#Example_of_using_replacer_parameter). The following will only compare all objects keys that are named "derp":

function areEqual(obj1, obj2, filter) {
    var a = JSON.stringify(obj1, filter), b = JSON.stringify(obj2, filter);
    if (!a) a = '';
    if (!b) b = '';
    return (a.split('').sort().join('') == b.split('').sort().join(''));
}
var equal = areEqual(obj1, obj2, function(key, value) {
    return (key === 'derp') ? value : undefined;
});
Redblooded answered 10/2, 2015 at 18:39 Comment(4)
Oh, I also forgot, but the function can be sped up by first testing object equaling and bailing early if they are the same object: if (obj1 === obj2) return true;Redblooded
areEqual({a: 'b'}, {b: 'a'}) gets true then?Derain
Yea, I realized after posting that this "solution" has issues. It needs a bit more work in the sorting algorithm to actually work properly.Redblooded
@Redblooded it's possible to edit your post if you want to improve it.Holdback
G
4

you can use _.isEqual(obj1, obj2) from the underscore.js library.

Here is an example:

var stooge = {name: 'moe', luckyNumbers: [13, 27, 34]};
var clone  = {name: 'moe', luckyNumbers: [13, 27, 34]};
stooge == clone;
=> false
_.isEqual(stooge, clone);
=> true

See the official documentation from here: http://underscorejs.org/#isEqual

Grizzled answered 20/3, 2018 at 18:28 Comment(0)
S
4

One easy way I have found to compare the values of two javascript objects while ignoring property order is with the JSON stringify replacer function:

const compareReplacer = (key, value) => {
    if(typeof value === 'object' && !(value instanceof Array))
        return Object.entries(value).sort();
    return value;
}
export const compareObjects = (a, b) => JSON.stringify(a, compareReplacer) === JSON.stringify(b, compareReplacer);

This will order the properties at every step of the way so that the string result will be invariant to property order. Some one has probably done this before but I just thought I would share it incase not :).

Sindhi answered 1/2, 2022 at 0:3 Comment(0)
C
3

Needing a more generic object comparison function than had been posted, I cooked up the following. Critique appreciated...

Object.prototype.equals = function(iObj) {
  if (this.constructor !== iObj.constructor)
    return false;
  var aMemberCount = 0;
  for (var a in this) {
    if (!this.hasOwnProperty(a))
      continue;
    if (typeof this[a] === 'object' && typeof iObj[a] === 'object' ? !this[a].equals(iObj[a]) : this[a] !== iObj[a])
      return false;
    ++aMemberCount;
  }
  for (var a in iObj)
    if (iObj.hasOwnProperty(a))
      --aMemberCount;
  return aMemberCount ? false : true;
}
Clos answered 27/5, 2010 at 12:0 Comment(1)
Be very careful about modifying Object.prototype -- in the vast majority of cases it is not advised (additions appear in all for..in loops, for example). Perhaps consider Object.equals = function(aObj, bObj) {...}?Timer
O
3

If you are comparing JSON objects you can use https://github.com/mirek/node-rus-diff

npm install rus-diff

Usage:

a = {foo:{bar:1}}
b = {foo:{bar:1}}
c = {foo:{bar:2}}

var rusDiff = require('rus-diff').rusDiff

console.log(rusDiff(a, b)) // -> false, meaning a and b are equal
console.log(rusDiff(a, c)) // -> { '$set': { 'foo.bar': 2 } }

If two objects are different, a MongoDB compatible {$rename:{...}, $unset:{...}, $set:{...}} like object is returned.

Organizer answered 11/3, 2014 at 17:44 Comment(0)
P
3

I faced the same problem and deccided to write my own solution. But because I want to also compare Arrays with Objects and vice-versa, I crafted a generic solution. I decided to add the functions to the prototype, but one can easily rewrite them to standalone functions. Here is the code:

Array.prototype.equals = Object.prototype.equals = function(b) {
    var ar = JSON.parse(JSON.stringify(b));
    var err = false;
    for(var key in this) {
        if(this.hasOwnProperty(key)) {
            var found = ar.find(this[key]);
            if(found > -1) {
                if(Object.prototype.toString.call(ar) === "[object Object]") {
                    delete ar[Object.keys(ar)[found]];
                }
                else {
                    ar.splice(found, 1);
                }
            }
            else {
                err = true;
                break;
            }
        }
    };
    if(Object.keys(ar).length > 0 || err) {
        return false;
    }
    return true;
}

Array.prototype.find = Object.prototype.find = function(v) {
    var f = -1;
    for(var i in this) {
        if(this.hasOwnProperty(i)) {
            if(Object.prototype.toString.call(this[i]) === "[object Array]" || Object.prototype.toString.call(this[i]) === "[object Object]") {
                if(this[i].equals(v)) {
                    f = (typeof(i) == "number") ? i : Object.keys(this).indexOf(i);
                }
            }
            else if(this[i] === v) {
                f = (typeof(i) == "number") ? i : Object.keys(this).indexOf(i);
            }
        }
    }
    return f;
}

This Algorithm is split into two parts; The equals function itself and a function to find the numeric index of a property in an array / object. The find function is only needed because indexof only finds numbers and strings and no objects .

One can call it like this:

({a: 1, b: "h"}).equals({a: 1, b: "h"});

The function either returns true or false, in this case true. The algorithm als allows comparison between very complex objects:

({a: 1, b: "hello", c: ["w", "o", "r", "l", "d", {answer1: "should be", answer2: true}]}).equals({b: "hello", a: 1, c: ["w", "d", "o", "r", {answer1: "should be", answer2: true}, "l"]})

The upper example will return true, even tho the properties have a different ordering. One small detail to look out for: This code also checks for the same type of two variables, so "3" is not the same as 3.

Pragmatism answered 13/9, 2016 at 17:21 Comment(0)
I
3

Assuming that the order of the properties in the object is not changed.

JSON.stringify() works for deep and non-deep both types of objects, not very sure of performance aspects:

var object1 = {
  key: "value"
};

var object2 = {
  key: "value"
};

var object3 = {
  key: "no value"
};

console.log('object1 and object2 are equal: ', JSON.stringify(object1) === JSON.stringify(object2));

console.log('object2 and object3 are equal: ', JSON.stringify(object2) === JSON.stringify(object3));
Incommunicado answered 17/5, 2018 at 7:32 Comment(2)
This does not do what OP wants, as it will only match if both objects have all the same keys, which they state they will not. It would also require the keys to be in the same order, which is also not really reasonable.Richerson
What is the properties are in different order ??? Nope not a good methodAshwell
F
3

How to determine that the partial object (Partial<T>) is equal to the original object (T) in typescript.

function compareTwoObjects<T>(original: T, partial: Partial<T>): boolean {
  return !Object.keys(partial).some((key) => partial[key] !== original[key]);
}

P.S. Initially I was planning to create a new question with an answer. But such a question already exists and marked as a duplicate.

Fatling answered 23/4, 2020 at 14:34 Comment(0)
D
3
let std1 = {
  name: "Abhijeet",
  roll: 1
}

let std2 = {
  name: "Siddharth",
  roll: 2
}

console.log(JSON.stringify(std1) === JSON.stringify(std2))
Delorsedelos answered 22/9, 2021 at 14:19 Comment(1)
⚠️ JSON.stringify({y: 1, x: 1}) !== JSON.stringify({x: 1, y: 1})Tamper
M
3

For short and simple:

const compare = (x, y) => {
  const srt = (obj) => JSON.stringify(obj)?.split('').sort().join('');
  return srt(x) === srt(y);
};

// ----- How to use ---
const a = {'one':1, 'two':2,'three':3};
const b = {'two':2, 'one':1, 'three':3}; //note same values as (const a)
const c = {'one':1, 'two':2,'three':3};
const d = {'one':1, 'two':2,'four':4};

compare(a, b); //true
compare(a, c); //true
compare(a, d); //false

//----- BUT! -----
JSON.stringify(a) === JSON.stringify(b); //false

//----- AND -----
compare({}, {}); //true
compare({}, undefined); //false
compare(undefined, undefined); //true
compare(undefined, ''); //false
compare(undefined, null); //false
compare(null, null); //true
compare('', ''); //true
Mistaken answered 21/5, 2022 at 12:46 Comment(2)
your solution will return true for objects {one: 'two'} and {two: 'one'}.Bimetallism
You are absolutely right. I will edit to fix.Mistaken
G
3

For short and simple, this will check equality of two variables no matter the type.

function isSame (obj1, obj2) {
  const obj1Keys = Object.keys(obj1)
  const obj2Keys = Object.keys(obj2)

  return obj1Keys.length === obj2Keys.length && obj1Keys.every((key) => obj1[key] === obj2[key])
}
Guzman answered 31/12, 2022 at 22:41 Comment(1)
Well, it's really a simple solutionZsigmondy
M
2

I'd advise against hashing or serialization (as the JSON solution suggest). If you need to test if two objects are equal, then you need to define what equals means. It could be that all data members in both objects match, or it could be that must the memory locations match (meaning both variables reference the same object in memory), or may be that only one data member in each object must match.

Recently I developed an object whose constructor creates a new id (starting from 1 and incrementing by 1) each time an instance is created. This object has an isEqual function that compares that id value with the id value of another object and returns true if they match.

In that case I defined "equal" as meaning the the id values match. Given that each instance has a unique id this could be used to enforce the idea that matching objects also occupy the same memory location. Although that is not necessary.

Mcglynn answered 19/11, 2008 at 16:20 Comment(0)
B
2

It's useful to consider two objects equal if they have all the same values for all properties and recursively for all nested objects and arrays. I also consider the following two objects equal:

var a = {p1: 1};
var b = {p1: 1, p2: undefined};

Similarly, arrays can have "missing" elements and undefined elements. I would treat those the same as well:

var c = [1, 2];
var d = [1, 2, undefined];

A function that implements this definition of equality:

function isEqual(a, b) {
    if (a === b) {
        return true;
    }

    if (generalType(a) != generalType(b)) {
        return false;
    }

    if (a == b) {
        return true;
    }

    if (typeof a != 'object') {
        return false;
    }

    // null != {}
    if (a instanceof Object != b instanceof Object) {
        return false;
    }

    if (a instanceof Date || b instanceof Date) {
        if (a instanceof Date != b instanceof Date ||
            a.getTime() != b.getTime()) {
            return false;
        }
    }

    var allKeys = [].concat(keys(a), keys(b));
    uniqueArray(allKeys);

    for (var i = 0; i < allKeys.length; i++) {
        var prop = allKeys[i];
        if (!isEqual(a[prop], b[prop])) {
            return false;
        }
    }
    return true;
}

Source code (including the helper functions, generalType and uniqueArray): Unit Test and Test Runner here.

Barbe answered 28/8, 2010 at 19:52 Comment(0)
S
2

I'm making the following assumptions with this function:

  1. You control the objects you are comparing and you only have primitive values (ie. not nested objects, functions, etc.).
  2. Your browser has support for Object.keys.

This should be treated as a demonstration of a simple strategy.

/**
 * Checks the equality of two objects that contain primitive values. (ie. no nested objects, functions, etc.)
 * @param {Object} object1
 * @param {Object} object2
 * @param {Boolean} [order_matters] Affects the return value of unordered objects. (ex. {a:1, b:2} and {b:2, a:1}).
 * @returns {Boolean}
 */
function isEqual( object1, object2, order_matters ) {
    var keys1 = Object.keys(object1),
        keys2 = Object.keys(object2),
        i, key;

    // Test 1: Same number of elements
    if( keys1.length != keys2.length ) {
        return false;
    }

    // If order doesn't matter isEqual({a:2, b:1}, {b:1, a:2}) should return true.
    // keys1 = Object.keys({a:2, b:1}) = ["a","b"];
    // keys2 = Object.keys({b:1, a:2}) = ["b","a"];
    // This is why we are sorting keys1 and keys2.
    if( !order_matters ) {
        keys1.sort();
        keys2.sort();
    }

    // Test 2: Same keys
    for( i = 0; i < keys1.length; i++ ) {
        if( keys1[i] != keys2[i] ) {
            return false;
        }
    }

    // Test 3: Values
    for( i = 0; i < keys1.length; i++ ) {
        key = keys1[i];
        if( object1[key] != object2[key] ) {
            return false;
        }
    }

    return true;
}
Spancel answered 27/9, 2013 at 1:7 Comment(0)
V
2

For comparing keys for simple key/value pairs object instances, I use:

function compareKeys(r1, r2) {
    var nloops = 0, score = 0;
    for(k1 in r1) {
        for(k2 in r2) {
            nloops++;
            if(k1 == k2)
                score++; 
        }
    }
    return nloops == (score * score);
};

Once keys are compared, a simple additional for..in loop is enough.

Complexity is O(N*N) with N is the number of keys.

I hope/guess objects I define won't hold more than 1000 properties...

Vachell answered 29/11, 2013 at 11:34 Comment(0)
P
2

This is an addition for all the above, not a replacement. If you need to fast shallow-compare objects without need to check extra recursive cases. Here is a shot.

This compares for: 1) Equality of number of own properties, 2) Equality of key names, 3) if bCompareValues == true, Equality of corresponding property values and their types (triple equality)

var shallowCompareObjects = function(o1, o2, bCompareValues) {
    var s, 
        n1 = 0,
        n2 = 0,
        b  = true;

    for (s in o1) { n1 ++; }
    for (s in o2) { 
        if (!o1.hasOwnProperty(s)) {
            b = false;
            break;
        }
        if (bCompareValues && o1[s] !== o2[s]) {
            b = false;
            break;
        }
        n2 ++;
    }
    return b && n1 == n2;
}
Phifer answered 21/12, 2013 at 1:9 Comment(0)
L
2

I know this is a bit old, but I would like to add a solution that I came up with for this problem. I had an object and I wanted to know when its data changed. "something similar to Object.observe" and what I did was:

function checkObjects(obj,obj2){
   var values = [];
   var keys = [];
   keys = Object.keys(obj);
   keys.forEach(function(key){
      values.push(key);
   });
   var values2 = [];
   var keys2 = [];
   keys2 = Object.keys(obj2);
   keys2.forEach(function(key){
      values2.push(key);
   });
   return (values == values2 && keys == keys2)
}

This here can be duplicated and create an other set of arrays to compare the values and keys. It is very simple because they are now arrays and will return false if objects have different sizes.

Lamentable answered 11/2, 2015 at 20:37 Comment(1)
This'll always return false, because arrays don't compare by value, e.g. [1,2] != [1,2].Gine
P
2

Pulling out from my personal library, which i use for my work repeatedly. The following function is a lenient recursive deep equal, which does not check

  • Class equality
  • Inherited values
  • Values strict equality

I mainly use this to check if i get equal replies against various API implementation. Where implementation difference (like string vs number) and additional null values, can occur.

Its implementation is quite straightforward and short (if all the comments is stripped off)

/** Recursively check if both objects are equal in value
***
*** This function is designed to use multiple methods from most probable 
*** (and in most cases) valid, to the more regid and complex method.
***
*** One of the main principles behind the various check is that while
*** some of the simpler checks such as == or JSON may cause false negatives,
*** they do not cause false positives. As such they can be safely run first.
***
*** # !Important Note:
*** as this function is designed for simplified deep equal checks it is not designed
*** for the following
***
*** - Class equality, (ClassA().a = 1) maybe valid to (ClassB().b = 1)
*** - Inherited values, this actually ignores them
*** - Values being strictly equal, "1" is equal to 1 (see the basic equality check on this)
*** - Performance across all cases. This is designed for high performance on the
***   most probable cases of == / JSON equality. Consider bench testing, if you have
***   more 'complex' requirments
***
*** @param  objA : First object to compare
*** @param  objB : 2nd object to compare
*** @param  .... : Any other objects to compare
***
*** @returns true if all equals, or false if invalid
***
*** @license Copyright by [email protected], 2012.
***          Licensed under the MIT license: http://opensource.org/licenses/MIT
**/
function simpleRecusiveDeepEqual(objA, objB) {
	// Multiple comparision check
	//--------------------------------------------
	var args = Array.prototype.slice.call(arguments);
	if(args.length > 2) {
		for(var a=1; a<args.length; ++a) {
			if(!simpleRecusiveDeepEqual(args[a-1], args[a])) {
				return false;
			}
		}
		return true;
	} else if(args.length < 2) {
		throw "simpleRecusiveDeepEqual, requires atleast 2 arguments";
	}
	
	// basic equality check,
	//--------------------------------------------
	// if this succed the 2 basic values is equal,
	// such as numbers and string.
	//
	// or its actually the same object pointer. Bam
	//
	// Note that if string and number strictly equal is required
	// change the equality from ==, to ===
	//
	if(objA == objB) {
		return true;
	}
	
	// If a value is a bsic type, and failed above. This fails
	var basicTypes = ["boolean", "number", "string"];
	if( basicTypes.indexOf(typeof objA) >= 0 || basicTypes.indexOf(typeof objB) >= 0 ) {
		return false;
	}
	
	// JSON equality check,
	//--------------------------------------------
	// this can fail, if the JSON stringify the objects in the wrong order
	// for example the following may fail, due to different string order:
	//
	// JSON.stringify( {a:1, b:2} ) == JSON.stringify( {b:2, a:1} )
	//
	if(JSON.stringify(objA) == JSON.stringify(objB)) {
		return true;
	}
	
	// Array equality check
	//--------------------------------------------
	// This is performed prior to iteration check,
	// Without this check the following would have been considered valid
	//
	// simpleRecusiveDeepEqual( { 0:1963 }, [1963] );
	//
	// Note that u may remove this segment if this is what is intended
	//
	if( Array.isArray(objA) ) {
		//objA is array, objB is not an array
		if( !Array.isArray(objB) ) {
			return false;
		}
	} else if( Array.isArray(objB) ) {
		//objA is not array, objB is an array
		return false;
	}
	
	// Nested values iteration
	//--------------------------------------------
	// Scan and iterate all the nested values, and check for non equal values recusively
	//
	// Note that this does not check against null equality, remove the various "!= null"
	// if this is required
	
	var i; //reuse var to iterate
	
	// Check objA values against objB
	for (i in objA) {
		//Protect against inherited properties
		if(objA.hasOwnProperty(i)) {
			if(objB.hasOwnProperty(i)) {
				// Check if deep equal is valid
				if(!simpleRecusiveDeepEqual( objA[i], objB[i] )) {
					return false;
				}
			} else if(objA[i] != null) {
				//ignore null values in objA, that objB does not have
				//else fails
				return false;
			}
		}
	}
	
	// Check if objB has additional values, that objA do not, fail if so
	for (i in objB) {
		if(objB.hasOwnProperty(i)) {
			if(objB[i] != null && !objA.hasOwnProperty(i)) {
				//ignore null values in objB, that objA does not have
				//else fails
				return false;
			}
		}
	}
	
	// End of all checks
	//--------------------------------------------
	// By reaching here, all iteration scans have been done.
	// and should have returned false if it failed
	return true;
}

// Sanity checking of simpleRecusiveDeepEqual
(function() {
	if(
		// Basic checks
		!simpleRecusiveDeepEqual({}, {}) ||
		!simpleRecusiveDeepEqual([], []) ||
		!simpleRecusiveDeepEqual(['a'], ['a']) ||
		// Not strict checks
		!simpleRecusiveDeepEqual("1", 1) ||
		// Multiple objects check
		!simpleRecusiveDeepEqual( { a:[1,2] }, { a:[1,2] }, { a:[1,2] } ) ||
		// Ensure distinction between array and object (the following should fail)
		simpleRecusiveDeepEqual( [1963], { 0:1963 } ) ||
		// Null strict checks
		simpleRecusiveDeepEqual( 0, null ) ||
		simpleRecusiveDeepEqual( "", null ) ||
		// Last "false" exists to make the various check above easy to comment in/out
		false
	) {
		alert("FATAL ERROR: simpleRecusiveDeepEqual failed basic checks");
	} else { 
		//added this last line, for SO snippet alert on success
		alert("simpleRecusiveDeepEqual: Passed all checks, Yays!");
	}
})();
Parakeet answered 17/4, 2015 at 15:14 Comment(0)
S
2

I see spaghetti code answers. Without using any third party libs, this is very easy.

Firstly sort the two objects by key their key names.

let objectOne = { hey, you }
let objectTwo = { you, hey }

// If you really wanted you could make this recursive for deep sort.
const sortObjectByKeyname = (objectToSort) => {
    return Object.keys(objectToSort).sort().reduce((r, k) => (r[k] = objectToSort[k], r), {});
}

let objectOne = sortObjectByKeyname(objectOne)
let objectTwo = sortObjectByKeyname(objectTwo)

Then simply use a string to compare them.

JSON.stringify(objectOne) === JSON.stringify(objectTwo)
Sisile answered 2/10, 2018 at 18:4 Comment(4)
This also does not work for deep copy, it has only one iteration depth.Heaver
I think @Heaver means you need to recursively sort keys of nested objects.Telethon
So, first recursively you sort the objects, then you allocate another part of the memory to literally duplicate both of these objects and converting them into strings, and then you compare them. Do you see nothing wrong with this? Like, what if it's an enormous object? JSON is supposed to let you check equality without having to first sort and then compare the convert strings. Very bad practices and bad performance, why do you have to use JSON.stringify in the first place.Crotchety
@eksapsy different problems have different solutions. Readability always trumps performance in coding, though, today; on modern systems, we have the memory to spare, optimizations root of evil in programming. I'm sure if you have "enormous" objects, then you're the whole architecture is probably wrong; you should consider using something like Elastic then for large JSON collections. There are many different solutions to this though, I'm sure this could be better, I wrote this hastly years ago.Sisile
Y
2

Here is a solution using ES6+

// this comparison would not work for function and symbol comparisons
// this would only work best for compared objects that do not belong to same address in memory
// Returns true if there is no difference, and false otherwise


export const isObjSame = (obj1, obj2) => {
    if (typeof obj1 !== "object" && obj1 !== obj2) {
        return false;
    }

    if (typeof obj1 !== "object" && typeof obj2 !== "object" && obj1 === obj2) {
        return true;
    }

    if (typeof obj1 === "object" && typeof obj2 === "object") {
        if (Array.isArray(obj1) && Array.isArray(obj2)) {
            if (obj1.length === obj2.length) {
                if (obj1.length === 0) {
                    return true;
                }
                const firstElemType = typeof obj1[0];

                if (typeof firstElemType !== "object") {
                    const confirmSameType = currentType =>
                        typeof currentType === firstElemType;

                    const checkObjOne = obj1.every(confirmSameType);
                    const checkObjTwo = obj2.every(confirmSameType);

                    if (checkObjOne && checkObjTwo) {
                        // they are primitves, we can therefore sort before and compare by index
                        // use number sort
                        // use alphabet sort
                        // use regular sort
                        if (firstElemType === "string") {
                            obj1.sort((a, b) => a.localeCompare(b));
                            obj2.sort((a, b) => a.localeCompare(b));
                        }
                        obj1.sort((a, b) => a - b);
                        obj2.sort((a, b) => a - b);

                        let equal = true;

                        obj1.map((element, index) => {
                            if (!isObjSame(element, obj2[index])) {
                                equal = false;
                            }
                        });

                        return equal;
                    }

                    if (
                        (checkObjOne && !checkObjTwo) ||
                        (!checkObjOne && checkObjTwo)
                    ) {
                        return false;
                    }

                    if (!checkObjOne && !checkObjTwo) {
                        for (let i = 0; i <= obj1.length; i++) {
                            const compareIt = isObjSame(obj1[i], obj2[i]);
                            if (!compareIt) {
                                return false;
                            }
                        }

                        return true;
                    }

                    // if()
                }
                const newValue = isObjSame(obj1, obj2);
                return newValue;
            } else {
                return false;
            }
        }

        if (!Array.isArray(obj1) && !Array.isArray(obj2)) {
            let equal = true;
            if (obj1 && obj2) {
                const allKeys1 = Array.from(Object.keys(obj1));
                const allKeys2 = Array.from(Object.keys(obj2));

                if (allKeys1.length === allKeys2.length) {
                    allKeys1.sort((a, b) => a - b);
                    allKeys2.sort((a, b) => a - b);

                    allKeys1.map((key, index) => {
                        if (
                            key.toLowerCase() !== allKeys2[index].toLowerCase()
                        ) {
                            equal = false;
                            return;
                        }

                        const confirmEquality = isObjSame(obj1[key], obj2[key]);

                        if (!confirmEquality) {
                            equal = confirmEquality;
                            return;
                        }
                    });
                }
            }

            return equal;

            // return false;
        }
    }
};
Yeah answered 19/3, 2021 at 11:6 Comment(0)
A
2

Although this question is sufficiently answered, I am missing one approach: the toJSON interface.

Usually you want to compare to object by stringifying them, because this is the fastest way. But ofter the comparison is considered to by false, because of the order of the properties.

const obj1 = {
  a: 1,
  b: 2,
  c: { 
    ca: 1,
    cb: 2
  }
}

const obj2 = {
  b: 2, // changed order with a
  a: 1,
  c: { 
    ca: 1,
    cb: 2
  }
}

JSON.stringify(obj1) === JSON.stringify(obj2) // false

Obviously the objects are considered to be different, because the order of property a and b differ.

To solve this, you can implement the toJSON interface, and define a deterministic output.

const obj1 = {
  a: 1,
  b: 2,
  c: { 
    ca: 1,
    cb: 2
  },
  toJSON() {
    return {
      a: this.a,
      b: this.b,
      c: { 
        ca: this.c.ca,
        cb: this.c.ca
      }
    }
  }
}

const obj2 = {
  b: 2,
  a: 1,
  c: { 
    ca: 1,
    cb: 2
  },
  toJSON() {
    return {
      a: this.a,
      b: this.b,
      c: { 
        ca: this.c.ca,
        cb: this.c.ca
      }
    }
  }
}

JSON.stringify(obj1) === JSON.stringify(obj2) // true

Et voila: the string representations of obj1 and obj2 are concidered the same.

TIP

If you do not have access to the direct generation of the object, you can simply attach the toJSON function:

obj1.toJSON = function() {
  return {
    a: this.a,
    b: this.b,
    c: { 
      ca: this.c.ca,
      cb: this.c.ca
    }
  }
}

obj2.toJSON = function() {
  return {
    a: this.a,
    b: this.b,
    c: { 
      ca: this.c.ca,
      cb: this.c.ca
    }
  }
}

JSON.stringify(obj1) === JSON.stringify(obj2) // true
Andras answered 25/6, 2021 at 12:14 Comment(1)
Does not seems really viable if not in trivial cases. Imagine having an object with many properties. Also, it does not solve the issue of equality between arraysCharbonnier
Z
2

stringify both objects and compare

return (JSON.stringify(obj1) === JSON.stringify(obj2))

This will return true or false

Zohar answered 3/2, 2022 at 5:5 Comment(3)
ob1 = {"age": 1, "name": "a" } ob2 = {"name": "a", "age": 1} JSON.stringify(ob1) == JSON.stringify(ob2) // FALSEPopcorn
order of the properties mattersItalic
Solution is simple, but not effective.Pediculosis
F
1

I'm not a Javascript expert, but here is one simple attempt to solve it. I check for three things:

  1. Is it an object and also that it is not null because typeof null is object.
  2. If the property count of the two objects is the same? If not they are not equal.
  3. Loop through properties of one and check if corresponding property has same value in second object.

function deepEqual (first, second) {
  // Not equal if either is not an object or is null.
  if (!isObject(first) || !isObject(second) ) return false;

  // If properties count is different
  if (keys(first).length != keys(second).length) return false;

  // Return false if any property value is different.
  for(prop in first){
    if (first[prop] != second[prop]) return false;
  }
  return true;
}

// Checks if argument is an object and is not null
function isObject(obj) {
  return (typeof obj === "object" && obj != null);
}

// returns arrays of object keys
function keys (obj) {
  result = [];
  for(var key in obj){
    result.push(key);
  }
  return result;
}

// Some test code
obj1 = {
  name: 'Singh',
  age: 20
}

obj2 = {
  age: 20,
  name: 'Singh'
}

obj3 = {
  name: 'Kaur',
  age: 19
}

console.log(deepEqual(obj1, obj2));
console.log(deepEqual(obj1, obj3));
Faria answered 24/7, 2015 at 19:54 Comment(1)
doesn't handle nested objectsCattle
T
1

In objects (without methods) we need to check for nested Objects, Arrays and primitive types. Objects can have other oblects and arrays (arrays also can include other objects and arrays), so we can use recursive function like in below: arrayEquals checks arrays for equality and equals checks objects equality:

function arrayEquals(a, b) {
    if (a.length != b.length) {
        return false;
    }
    for (let i = 0; i < a.length; i++) {
        if (a[i].constructor !== b[i].constructor) {
            return false;
        }

        if (a[i] instanceof Array && b[i] instanceof Array) {
            if (!arrayEquals(a, b)) {
                return false;
            }
        } else if (a[i] instanceof Object && b[i] instanceof Object) {
            if (!equals(a[i], b[i])) {
                return false;
            }
        } else if (a[i] !== b[i]) {
            return false;
        }
    }
    return true;
}

function equals(a, b) {
    for (let el in a) {
        if (b.hasOwnProperty(el)) {
            if (a[el].constructor !== b[el].constructor) {
                return false;
            }

            if (a[el] instanceof Array && b[el] instanceof Array) {
                if (!arrayEquals(a[el], b[el])) {
                    return false;
                }
            } else if (a[el] instanceof Object && b[el] instanceof Object) {
                if (!equals(a[el], b[el])) {
                    return false;
                }
            } else if (a[el] !== b[el]) {
                return false;
            }
        } else {
            return false;
        }
    }
    return true;
}

Imagine you have two objects:

let a = {
    a: 1,
    b: { c: 1, d: "test" },
    c: 3,
    d: [{ a: [1, 2], e: 2 }, "test", { c: 3, q: 5 }],
};

let b = {
    a: 1,
    b: { c: 1, d: "test" },
    c: 3,
    d: [{ a: [1, 2], e: 2 }, "test", { c: 3, q: 5 }],
};

And here using above equals function, you can easily compare two of these objects like this:

if(equals(a, b)) {
    // do whatever you want
}
Triplane answered 8/8, 2021 at 11:39 Comment(0)
T
1

In React, you can use isEqual from 'react-fast-compare'. This answer is probably not applicable to plain JavaScript, but can be useful in case you are using React.

console.log(isEqual({ hello: 'world' }, { hello: 'world' })) // returns true

The fastest deep equal comparison for React. Very quick general-purpose deep comparison, too. Great for React.memo and shouldComponentUpdate.

More information can be found here: https://www.npmjs.com/package/react-fast-compare.

Ticktack answered 12/8, 2021 at 14:3 Comment(1)
And if you don't use React, just use the package 'react-fast-compare' is based on github.com/epoberezkin/fast-deep-equalInsolvable
F
1

2022:

I came up with a breeze dead-simple algorithm that addresses the most edge cases.

Steps:

  1. flatten the objects
  2. simple compare the two flattened objects and look for differences

If you saved the flatted object you can repeat using it.

let obj1= {var1:'value1', var2:{ var1:'value1', var2:'value2'}};
let obj2 = {var1:'value1', var2:{ var1:'value11',var2:'value2'}} 

let flat1= flattenObject(obj1)
/*
{
 'var1':'value1',
 'var2.var1':'value1',
 'var2.var2':'value2'
}
*/
let flat2= flattenObject(obj2)
/*
{
 'var1':'value1',
 'var2.var1':'value11',
 'var2.var2':'value2'
}
*/
isEqual(flat1, flat2)
/*
 false
*/

of course you can come with your implementations for that steps. but here is mine:

Implementations

function flattenObject(obj) {
 const object = Object.create(null);
 const path = [];
 const isObject = (value) => Object(value) === value;

 function dig(obj) {
  for (let [key, value] of Object.entries(obj)) {
    path.push(key);
    if (isObject(value)) dig(value);
    else object[path.join('.')] = value;
    path.pop();
  }
 }

 dig(obj);
 return object;
}
 function isEqual(flat1, flat2) {
    for (let key in flat2) {
        if (flat1[key] !== flat2[key])
            return false
    }
    // check for missing keys
    for (let key in flat1) {
        if (!(key in flat2))
            return false
    }
    return true
}

You can use that method to also get the Diff object between obj1 and obj2.

Look at that answer for details: Generic deep diff between two objects

Formulary answered 18/2, 2022 at 17:30 Comment(2)
What do you mean by "breeze"? Not "brief?Baud
breeze because the standart solution of recurtion are exhausting and with a lot of edge caseFormulary
S
1
const isEqual = function (var1, var2) {
    if (typeof var1 === 'object' && typeof var2 === 'object') {
        // Checking equality for each of the inner values of the objects
        const keys = [...new Set([...Object.keys(var1),...Object.keys(var2)])];
        return keys.every(key => isEqual(var1[key], var2[key]) && isEqual(var2[key], var1[key]));
    } else { // Primitive types (number, boolean etc..)
        return var1 === var2; // Normal equality
    }
}

So I've seen so many great answers here, but I couldn't find a short function, so I created one. This will check equality of two variables no matter the type. Hope you see it fit.

Stefanistefania answered 3/7, 2022 at 6:31 Comment(1)
Why do you call isEqual twice? if a==b then b==a, so this is redundant. Other than that it's a good answer for simple objects without arrays, symbols, functions, etc.Chesson
C
0

Here is a very basic approach to checking an object's "value equality".

var john = {
    occupation: "Web Developer",
    age: 25
};

var bobby = {
    occupation: "Web Developer",
    age: 25
};

function isEquivalent(a, b) {
    // Create arrays of property names

    var aProps = Object.getOwnPropertyNames(a);
    var bProps = Object.getOwnPropertyNames(b);

    // If number of properties is different, objects are not equivalent

    if (aProps.length != bProps.length) {
        return false;
    }

    for (var i = 0; i < aProps.length; i++) {
        var propName = aProps[i];

        // If values of same property are not equal, objects are not equivalent
        if (a[propName] !== b[propName]) {
           return false;
        }
    }

    // If we made it this far, objects are considered equivalent
    return true;
}

// Outputs: true
console.log(isEquivalent(john, bobby));

Demo - JSFiddle

As you can see, to check the objects' "value equality" we essentially have to iterate over every property in the objects to see whether they are equal. And while this simple implementation works for our example, there are a lot of cases that it doesn't handle. For instance:

  • What if one of the property values is itself an object?
  • What if one of the property values is NaN (the only value in JavaScript that is not equal to itself?)
  • What if a has a property with value undefined, while b doesn't have this property (which thus evaluates to undefined?)

For a robust method of checking objects' "value equality" it is better to rely on a well-tested library that covers the various edge cases like Underscore.

var john = {
    occupation: "Web Developer",
    age: 25
};

var bobby = {
    occupation: "Web Developer",
    age: 25
};

// Outputs: true
console.log(_.isEqual(john, bobby));

Demo - JSFiddle

Castile answered 7/5, 2014 at 7:6 Comment(1)
doesn't handle nested objectsCattle
C
0

My version, which includes the chain of where the difference is found, and what the difference is.

function DeepObjectCompare(O1, O2)
{
    try {
        DOC_Val(O1, O2, ['O1->O2', O1, O2]);
        return DOC_Val(O2, O1, ['O2->O1', O1, O2]);
    } catch(e) {
        console.log(e.Chain);
        throw(e);
    }
}
function DOC_Error(Reason, Chain, Val1, Val2)
{
    this.Reason=Reason;
    this.Chain=Chain;
    this.Val1=Val1;
    this.Val2=Val2;
}

function DOC_Val(Val1, Val2, Chain)
{
    function DoThrow(Reason, NewChain) { throw(new DOC_Error(Reason, NewChain!==undefined ? NewChain : Chain, Val1, Val2)); }

    if(typeof(Val1)!==typeof(Val2))
        return DoThrow('Type Mismatch');
    if(Val1===null || Val1===undefined)
        return Val1!==Val2 ? DoThrow('Null/undefined mismatch') : true;
    if(Val1.constructor!==Val2.constructor)
        return DoThrow('Constructor mismatch');
    switch(typeof(Val1))
    {
        case 'object':
            for(var m in Val1)
            {
                if(!Val1.hasOwnProperty(m))
                    continue;
                var CurChain=Chain.concat([m]);
                if(!Val2.hasOwnProperty(m))
                    return DoThrow('Val2 missing property', CurChain);
                DOC_Val(Val1[m], Val2[m], CurChain);
            }
            return true;
        case 'number':
            if(Number.isNaN(Val1))
                return !Number.isNaN(Val2) ? DoThrow('NaN mismatch') : true;
        case 'string':
        case 'boolean':
            return Val1!==Val2 ? DoThrow('Value mismatch') : true;
        case 'function':
            if(Val1.prototype!==Val2.prototype)
                return DoThrow('Prototype mismatch');
            if(Val1!==Val2)
                return DoThrow('Function mismatch');
            return true;
        default:
            return DoThrow('Val1 is unknown type');
    }
}
Concerned answered 24/9, 2016 at 6:37 Comment(0)
O
0

I've implemented a method that takes two jsons and checks to see if their keys have the same values using recursion. I used another question to solve this.

const arraysEqual = (a, b) => {
    if (a === b)
        return true;
    if (a === null || b === null)
        return false;
    if (a.length !== b.length)
        return false;

    // If you don't care about the order of the elements inside
    // the array, you should sort both arrays here.

    for (let i = 0; i < a.length; ++i) {
        if (a[i] !== b[i])
            return false;
    }
    return true;
};

const jsonsEqual = (a, b) => {

  if(typeof a !== 'object' || typeof b !== 'object')
      return false;

  if (Object.keys(a).length === Object.keys(b).length) { // if items have the same size
      let response = true;

      for (let key in a) {
          if (!b[key]) // if not key
              response = false;
          if (typeof a[key] !== typeof b[key]) // if typeof doesn't equals
              response = false;
          else {
              if (Array.isArray(a[key])) // if array
                  response = arraysEqual(a[key], b[key]);
              else if (typeof a[key] === 'object') // if another json
                  response = jsonsEqual(a[key], b[key]);
              else if (a[key] !== b[key])  // not equals
                  response = false;
          }
          if (!response) // return if one item isn't equal
              return false;
      }
  } else
      return false;

  return true;
};

const json1 = { 
  a: 'a', 
  b: 'asd', 
  c: [
    '1',
    2,
    2.5,
    '3', 
    {
      d: 'asd',
      e: [
        1.6,
        { 
          f: 'asdasd',
          g: '123'
        }
      ]
    }
  ],
  h: 1,
  i: 1.2,
};

const json2 = {
  a: 'nops',
  b: 'asd'
};

const json3 = { 
  a: 'h', 
  b: '484', 
  c: [
    3,
    4.5,
    '2ss', 
    {
      e: [
        { 
          f: 'asdasd',
          g: '123'
        }
      ]
    }
  ],
  h: 1,
  i: 1.2,
};

const result = jsonsEqual(json1,json2);
//const result = jsonsEqual(json1,json3);
//const result = jsonsEqual(json1,json1);

if(result) // is equal
  $('#result').text("Jsons are the same")
else
  $('#result').text("Jsons aren't equals")
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

<div id="result"></div>
Owings answered 29/8, 2017 at 3:4 Comment(0)
C
0

Here's a version of the stringify trick that is less typing and works in a lot of cases for trivial JSON data comparisons.

var obj1Fingerprint = JSON.stringify(obj1).replace(/\{|\}/g,'').split(',').sort().join(',');
var obj2Fingerprint = JSON.stringify(obj2).replace(/\{|\}/g,'').split(',').sort().join(',');
if ( obj1Fingerprint === obj2Fingerprint) { ... } else { ... }
Cambyses answered 8/1, 2018 at 22:15 Comment(2)
doesn't handle nested objectsCattle
And very horrible performance footprint. Bad practice in general, to have to first convert to convert both of those objects to string, allocating x4 times the memory, and then to sort both strings, and then compare the string of those objects. Like, if we were in another language, everyone would be like "what the f***", but apparently doing that in JS it's completely ok, since every single answer here includes at least once "JSON.stringify".Crotchety
D
0

This is a simple Javascript function to compare two objects having simple key-value pairs. The function will return an array of strings, where each string is a path to an inequality between the two objects.

function compare(a,b) {
    var paths = [];
    [...new Set(Object.keys(a).concat(Object.keys(b)))].forEach(key=>{
        if(typeof a[key] === 'object' && typeof b[key] === 'object') {
            var results = compare(a[key], b[key]);
            if(JSON.stringify(results)!=='[]') {
                paths.push(...results.map(result=>key.concat("=>"+result)));
            }
        }
        else if (a[key]!==b[key]) {
            paths.push(key);
        }
    })
    return paths;
}

If you only want to compare two objects without knowing the paths to inequalities, you can do it as follows:

if(JSON.stringify(compare(object1, object2))==='[]') {
   // the two objects are equal
} else {
   // the two objects are not equal
}
Donne answered 9/9, 2019 at 17:40 Comment(0)
T
0
  1. sort the objects (dictionary)
  2. compare JSON string

    function areTwoDictsEqual(dictA, dictB) {
        function sortDict(dict) {
            var keys = Object.keys(dict);
            keys.sort();
    
            var newDict = {};
            for (var i=0; i<keys.length; i++) {
                var key = keys[i];
                var value = dict[key];
                newDict[key] = value;
            } 
            return newDict;
        }
    
        return JSON.stringify(sortDict(dictA)) == JSON.stringify(sortDict(dictB));
    }
    
Transcribe answered 6/10, 2019 at 0:52 Comment(0)
L
0

I just wrote this method just to be sure that arrays and objects are both compared in a clear way.

This should do the trick as well! :)

public class Objects {
    /**
     * Checks whether a value is of type Object
     * @param value the value
     */
    public static isObject = (value: any): boolean => {
        return value === Object(value) && Object.prototype.toString.call(value) !== '[object Array]'
    }

    /**
     * Checks whether a value is of type Array
     * @param value the value
     */
    public static isArray = (value: any): boolean => {
        return Object.prototype.toString.call(value) === '[object Array]' && !Objects.isObject(value)
    }

    /**
     * Check whether two values are equal
     */
    public static isEqual = (objectA: any, objectB: any) => {
        // Objects
        if (Objects.isObject(objectA) && !Objects.isObject(objectB)) {
            return false
        }
        else if (!Objects.isObject(objectA) && Objects.isObject(objectB)) {
            return false
        }
        // Arrays
        else if (Objects.isArray(objectA) && !Objects.isArray(objectB)) {
            return false
        }
        else if (!Objects.isArray(objectA) && Objects.isArray(objectB)) {
            return false
        }
        // Primitives
        else if (!Objects.isArray(objectA) && !Objects.isObject(objectA)) {
            return objectA === objectB
        }
        // Object or array
        else {
            const compareObject = (objectA: any, objectB: any): boolean => {
                if (Object.keys(objectA).length !== Object.keys(objectB).length) return false

                for (const propertyName of Object.keys(objectA)) {
                    const valueA = objectA[propertyName]
                    const valueB = objectB[propertyName]

                    if (!Objects.isEqual(valueA, valueB)) {
                        return false
                    }
                }

                return true
            }
            const compareArray = (arrayA: any[], arrayB: any[]): boolean => {
                if (arrayA.length !== arrayB.length) return false

                for (const index in arrayA) {
                    const valueA = arrayA[index]
                    const valueB = arrayB[index]

                    if (!Objects.isEqual(valueA, valueB)) {
                        return false
                    }
                }

                return true
            }
            if (Objects.isObject(objectA)) {
                return compareObject(objectA, objectB)
            } else {
                return compareArray(objectA, objectB)
            }
        }
    }
}
Laryngo answered 4/6, 2020 at 19:37 Comment(0)
T
0

One additional option, is use equals of Ramda library:

const c = {a: 1, b: 2};
const d = {b: 2, a: 1};
R.equals(c, d); //=> true
Triplex answered 24/11, 2020 at 13:41 Comment(0)
L
0

const obj = {
  name: "Carl",
  age: 15
}
const obj2 = {
  name: "Carl",
  age: 15,
}


const compareObj = (objects) => {
  const res =  objects.map((item) => {
    return Object.entries(item).flat().join()
  })
  return res.every((a) => {
    return a === res[0]
  })
}

console.log(compareObj([obj,obj2]))
Lambent answered 13/2, 2021 at 16:47 Comment(3)
this is not recursive for internal subobject elementsOverijssel
@LeninZapata, what do you mean?Lambent
not working for internal objects and sub-objectsOverijssel
F
0

Pure JS approach: My answer is based on generating a string which returns the same value whether the attribute order is same or not. The settings object can be used to switch whether the case and the presence of white space matters. (To avoid losing focus I'm not including those support functions, or the isObject which I guess should be in any utility set.)

Also not shown here, but to reduce the string comparison time, if the objects are large and you want to speed up the comparison, you could also hash the strings and compare substrings; this would make sense to do only for very large objects (and of course a small chance of false equality).

You can then just compare genObjStr(obj1) ?= genObjStr(obj2)

function genObjStr(obj, settings) {
// Generate a string that corresponds to an object guarenteed to be the same str even if
// the object have different ordering. The string would largely be used for comparison purposes

var settings = settings||{};
var doStripWhiteSpace = defTrue(settings.doStripWhiteSpace);
var doSetLowerCase = settings.doSetLowerCase||false;

if(isArray(obj)) {
    var vals = [];
    for(var i = 0; i < obj.length; ++i) {
        vals.push(genObjStr(obj[i], settings));
    }
    vals = arraySort(vals);
    return vals.join(`,`);

} else if(isObject(obj)) {

    var keys = Object.keys(obj);
    keys = arraySort(keys);

    var vals = [];
    for(var key of keys) {
        
        var value = obj[key];
        
        value = genObjStr(value, settings);

        if(doStripWhiteSpace) {
            key = removeWhitespace(key);
            var value = removeWhitespace(value);
        };
        if(doSetLowerCase) {
            key = key.toLowerCase();
            value = value.toLowerCase();
        }

        vals.push(value);
    }
    var str = JSON.stringify({keys: keys, vals: vals});
    return str
} else {
    if(doStripWhiteSpace) {
        obj = removeWhitespace(obj);
    };
    if(doSetLowerCase) {
        obj = obj.toLowerCase();
    }
    return obj
}

}

var obj1 = {foo: 123, bar: `Test`};
var obj2 = {bar: `Test`, foo: 123};

console.log(genObjStr(obj1) == genObjStr(obj1))
Fibrilla answered 28/8, 2021 at 6:24 Comment(0)
S
0

If your question is to check if two objects are equal then this function might be useful:

function equals(a, b) {
    const aKeys = Object.keys(a)
    const bKeys = Object.keys(b)
    if(aKeys.length != bKeys.length) {
        return false
    }
    for(let i = 0;i < aKeys.length; i++) {
        if(aKeys[i] != bKeys[i]) {
            return false
        }
    }
    for(let i = 0;i < aKeys.length; i++) {
        if(a[aKeys[i]] != b[bKeys[i]]) {
            return false
        }
    }
    return true
}

First we check if the length of the list of keys of these objects is the same. If not, we return false.

To check if two objects are equal, they must have the same keys (=names) and the same values of the keys, so we get all the keys of objA, and objB and then we check if they are equal once we find that the two keys are not equal then we return false.

And then when all the keys are equal then we loop through one of the keys of one of the objects. Then we check if they are equal. Once they are not, we return false and after the two loops finished this means they are equal and we return true.

Note: this function works with only objects without any functions

Scarlet answered 3/11, 2021 at 18:36 Comment(0)
J
0

Using JSON.stringify() is not always reliable. So this method is the best solution to your question IMO

First of all, No, there is no generic means to determine that an object is equal!

But there is a concept called Shallow Equality Comparison. There is an npm library out there that can help you use this concept

Example

const shallowequal = require('shallowequal');
 
const object = { 'user': 'fred' };
const other = { 'user': 'fred' };

// Referential Equality Comparison (`strict ===`)
object === other; // → false

// Shallow Equality Comparison
shallowequal(object, other); // → true

If you want to know how to create shallowEqual comparision method, then refer here. Its from the opensource fbjs Facebook library.


Shallow Equality Comparision

shallowequal(obj1, obj2)

shallowEqual Performs a shallow equality comparison between two values (i.e. obj1 and obj2) to determine if they are equivalent.

The equality is performed by iterating through keys on the given obj1, and returning false whenever any key has values which are not strictly equal between obj1 and obj2. Otherwise, return true whenever the values of all keys are strictly equal.

Judaism answered 22/1, 2022 at 18:33 Comment(0)
D
-1

Depends on what you mean by equality. And therefore it is up to you, as the developer of the classes, to define their equality.

There's one case used sometimes, where two instances are considered 'equal' if they point to the same location in memory, but that is not always what you want. For instance, if I have a Person class, I might want to consider two Person objects 'equal' if they have the same Last Name, First Name, and Social Security Number (even if they point to different locations in memory).

On the other hand, we can't simply say that two objects are equal if the value of each of their members is the same, since, sometimes, you don't want that. In other words, for each class, it's up to the class developer to define what members make up the objects 'identity' and develop a proper equality operator (be it via overloading the == operator or an Equals method).

Saying that two objects are equal if they have the same hash is one way out. However you then have to wonder how the hash is calculated for each instance. Going back to the Person example above, we could use this system if the hash was calculated by looking at the values of the First Name, Last Name, and Social Security Number fields. On top of that, we are then relying on the quality of the hashing method (that's a huge topic on its own, but suffice it to say that not all hashes are created equal, and bad hashing methods can lead to more collisions, which in this case would return false matches).

Decembrist answered 14/10, 2008 at 13:54 Comment(0)
B
-1

I need to mock jQuery POST requests, so the equality that matters to me is that both objects have the same set of properties (none missing in either object), and that each property value is "equal" (according to this definition). I don't care about the objects having mismatching methods.

Here's what I'll be using, it should be good enough for my specific requirements:

function PostRequest() {
    for (var i = 0; i < arguments.length; i += 2) {
        this[arguments[i]] = arguments[i+1];
    }

    var compare = function(u, v) {
        if (typeof(u) != typeof(v)) {
            return false;
        }

        var allkeys = {};
        for (var i in u) {
            allkeys[i] = 1;
        }
        for (var i in v) {
            allkeys[i] = 1;
        }
        for (var i in allkeys) {
            if (u.hasOwnProperty(i) != v.hasOwnProperty(i)) {
                if ((u.hasOwnProperty(i) && typeof(u[i]) == 'function') ||
                    (v.hasOwnProperty(i) && typeof(v[i]) == 'function')) {
                    continue;
                } else {
                    return false;
                }
            }
            if (typeof(u[i]) != typeof(v[i])) {
                return false;
            }
            if (typeof(u[i]) == 'object') {
                if (!compare(u[i], v[i])) {
                    return false;
                }
            } else {
                if (u[i] !== v[i]) {
                    return false;
                }
            }
        }

        return true;
    };

    this.equals = function(o) {
        return compare(this, o);
    };

    return this;
}

Use like so:

foo = new PostRequest('text', 'hello', 'html', '<p>hello</p>');
foo.equals({ html: '<p>hello</p>', text: 'hello' });
Bathometer answered 12/1, 2010 at 13:54 Comment(0)
L
-1

I've written a small library that runs on Node.js and the browser called compare.js. It offers the usual comparison operators, such as ==, !=, >, >=, <, <= and identity on all data types of JavaScript.

E.g., you can use

cmp.eq(obj1, obj2);

and this will check for equality (using a deep-equal approach). Otherwise, if you do

cmp.id(obj1, obj2);

it will compare by reference, hence check for identity. You can also use < and > on objects, which mean subset and superset.

compare.js is covered by nearly 700 unit tests, hence it should hopefully not have too many bugs ;-).

You can find it on https://github.com/goloroden/compare.js for free, it is open-sourced under the MIT license.

Lonnalonnard answered 25/8, 2012 at 5:5 Comment(0)
P
-1

A quick "hack" to tell if two objects are similar, is to use their toString() methods. If you're checking objects A and B, make sure A and B have meaningful toString() methods and check that the strings they return are the same.

This isn't a panacea, but it can be useful sometimes in the right situations.

Portative answered 20/2, 2013 at 18:44 Comment(0)
G
-1

Some of the following solutions have problems with performance, functionality and style... They are not thought through enough, and some of them fail for different cases. I tried to address this problem in my own solution, and I would really much appreciate your feedback:

http://stamat.wordpress.com/javascript-object-comparison/

//Returns the object's class, Array, Date, RegExp, Object are of interest to us
var getClass = function(val) {
    return Object.prototype.toString.call(val)
        .match(/^\[object\s(.*)\]$/)[1];
};

//Defines the type of the value, extended typeof
var whatis = function(val) {

    if (val === undefined)
        return 'undefined';
    if (val === null)
        return 'null';

    var type = typeof val;

    if (type === 'object')
        type = getClass(val).toLowerCase();

    if (type === 'number') {
        if (val.toString().indexOf('.') > 0)
            return 'float';
        else
        return 'integer';
    }

    return type;
   };

var compareObjects = function(a, b) {
    if (a === b)
        return true;
    for (var i in a) {
        if (b.hasOwnProperty(i)) {
            if (!equal(a[i],b[i])) return false;
        } else {
            return false;
        }
    }

    for (var i in b) {
        if (!a.hasOwnProperty(i)) {
            return false;
        }
    }
    return true;
};

var compareArrays = function(a, b) {
    if (a === b)
        return true;
    if (a.length !== b.length)
        return false;
    for (var i = 0; i < a.length; i++){
        if(!equal(a[i], b[i])) return false;
    };
    return true;
};

var _equal = {};
_equal.array = compareArrays;
_equal.object = compareObjects;
_equal.date = function(a, b) {
    return a.getTime() === b.getTime();
};
_equal.regexp = function(a, b) {
    return a.toString() === b.toString();
};
//  uncoment to support function as string compare
//  _equal.fucntion =  _equal.regexp;



/*
 * Are two values equal, deep compare for objects and arrays.
 * @param a {any}
 * @param b {any}
 * @return {boolean} Are equal?
 */
var equal = function(a, b) {
    if (a !== b) {
        var atype = whatis(a), btype = whatis(b);

        if (atype === btype)
            return _equal.hasOwnProperty(atype) ? _equal[atype](a, b) : a==b;

        return false;
    }

    return true;
};
Gherardi answered 23/6, 2013 at 12:55 Comment(0)
I
-1

Object equality check:JSON.stringify(array1.sort()) === JSON.stringify(array2.sort())

The above test also works with arrays of objects in which case use a sort function as documented in http://www.w3schools.com/jsref/jsref_sort.asp

Might suffice for small arrays with flat JSON schemas.

Ingrowing answered 13/2, 2014 at 18:6 Comment(2)
Not guaranteed to work - object properties are unordered [specific order never guaranteed] according to the EcmaScript 3 spec.Timer
lots of issues see https://mcmap.net/q/36980/-how-can-i-determine-equality-for-two-javascript-objectsCattle
G
-1

Sure, while we're at it I'll throw in my own reinvention of the wheel (I'm proud of the number of spokes and materials used):

////////////////////////////////////////////////////////////////////////////////

var equals = function ( objectA, objectB ) {
    var result = false,
        keysA,
        keysB;

    // Check if they are pointing at the same variable. If they are, no need to test further.
    if ( objectA === objectB ) {
        return true;
    }

    // Check if they are the same type. If they are not, no need to test further.
    if ( typeof objectA !== typeof objectB ) {
        return false;
    }

    // Check what kind of variables they are to see what sort of comparison we should make.
    if ( typeof objectA === "object" ) {
        // Check if they have the same constructor, so that we are comparing apples with apples.
        if ( objectA.constructor === objectA.constructor ) {
            // If we are working with Arrays...
            if ( objectA instanceof Array ) {
                // Check the arrays are the same length. If not, they cannot be the same.
                if ( objectA.length === objectB.length ) {
                    // Compare each element. They must be identical. If not, the comparison stops immediately and returns false.
                    return objectA.every(
                        function ( element, i ) {
                            return equals( element, objectB[ i ] );
                        }
                    );
                }
                // They are not the same length, and so are not identical.
                else {
                    return false;
                }
            }
            // If we are working with RegExps...
            else if ( objectA instanceof RegExp ) {
                // Return the results of a string comparison of the expression.
                return ( objectA.toString() === objectB.toString() );
            }
            // Else we are working with other types of objects...
            else {
                // Get the keys as arrays from both objects. This uses Object.keys, so no old browsers here.
                keysA = Object.keys( objectA );

                keysB = Object.keys( objectB );

                // Check the key arrays are the same length. If not, they cannot be the same.
                if ( keysA.length === keysB.length ) {
                    // Compare each property. They must be identical. If not, the comparison stops immediately and returns false.
                    return keysA.every(
                        function ( element ) {
                            return equals( objectA[ element ], objectB[ element ] );
                        }
                    );
                }
                // They do not have the same number of keys, and so are not identical.
                else {
                    return false;
                }
            }
        }
        // They don't have the same constructor.
        else {
            return false;
        }
    }
    // If they are both functions, let us do a string comparison.
    else if ( typeof objectA === "function" ) {
        return ( objectA.toString() === objectB.toString() );
    }
    // If a simple variable type, compare directly without coercion.
    else {
        return ( objectA === objectB );
    }

    // Return a default if nothing has already been returned.
    return result;
};

////////////////////////////////////////////////////////////////////////////////

It returns false as quickly as possible, but of course for a large object where the difference is deeply nested it could be less effective. In my own scenario, good handling of nested arrays is important.

Hope it helps someone needing this kind of 'wheel'.

Gimcrack answered 19/11, 2014 at 10:30 Comment(0)
L
-1

Yeah, another answer...

Object.prototype.equals = function (object) {
    if (this.constructor !== object.constructor) return false;
    if (Object.keys(this).length !== Object.keys(object).length) return false;
    var obk;
    for (obk in object) {
        if (this[obk] !== object[obk])
            return false;
    }
    return true;
}

var aaa = JSON.parse('{"name":"mike","tel":"1324356584"}');
var bbb = JSON.parse('{"tel":"1324356584","name":"mike"}');
var ccc = JSON.parse('{"name":"mike","tel":"584"}');
var ddd = JSON.parse('{"name":"mike","tel":"1324356584", "work":"nope"}');

$("#ab").text(aaa.equals(bbb));
$("#ba").text(bbb.equals(aaa));
$("#bc").text(bbb.equals(ccc));
$("#ad").text(aaa.equals(ddd));
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
aaa equals bbb? <span id="ab"></span> <br/>
bbb equals aaa? <span id="ba"></span> <br/>
bbb equals ccc? <span id="bc"></span> <br/>
aaa equals ddd? <span id="ad"></span>
Lucknow answered 12/3, 2015 at 9:49 Comment(1)
doesn't handle nested objectsCattle
E
-1

I have a much shorter function that will go deep into all sub objects or arrays. It is as efficient as JSON.stringify(obj1) === JSON.stringify(obj2) but JSON.stringify will not work if the order is not the same (as mentioned here).

var obj1 = { a : 1, b : 2 };
var obj2 = { b : 2, a : 1 };

console.log(JSON.stringify(obj1) === JSON.stringify(obj2)); // false

The function would also be a good start if you want to do something with the unequal values.

function arr_or_obj(v)
{ return !!v && (v.constructor === Object || v.constructor === Array); }

function deep_equal(v1, v2)
{
    if (arr_or_obj(v1) && arr_or_obj(v2) && v1.constructor === v2.constructor)
    {
        if (Object.keys(v1).length === Object.keys(v2).length) // check the length
        for (var i in v1)
        {
            if (!deep_equal(v1[i], v2[i]))
            { return false; }
        }
        else
        { return false; }
    }
    else if (v1 !== v2)
    { return false; }

    return true;
}

//////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////

var obj1 = [
    {
        hat : {
            cap : ['something', null ],
            helmet : [ 'triple eight', 'pro-tec' ]
        },
        shoes : [ 'loafer', 'penny' ]
    },
    {
        beers : [ 'budweiser', 'busch' ],
        wines : [ 'barefoot', 'yellow tail' ]
    }
];

var obj2 = [
    {
        shoes : [ 'loafer', 'penny' ], // same even if the order is different
        hat : {
            cap : ['something', null ],
            helmet : [ 'triple eight', 'pro-tec' ]
        }
    },
    {
        beers : [ 'budweiser', 'busch' ],
        wines : [ 'barefoot', 'yellow tail' ]
    }
];

console.log(deep_equal(obj1, obj2)); // true
console.log(JSON.stringify(obj1) === JSON.stringify(obj2)); // false
console.log(deep_equal([], [])); // true
console.log(deep_equal({}, {})); // true
console.log(deep_equal([], {})); // false

And if you want to add support for Function, Date and RegExp, you can add this at the beginning of deep_equal (not tested):

if ((typeof obj1 === 'function' && typeof obj2 === 'function') ||
(obj1 instanceof Date && obj2 instanceof Date) ||
(obj1 instanceof RegExp && obj2 instanceof RegExp))
{
    obj1 = obj1.toString();
    obj2 = obj2.toString();
}
Enterostomy answered 29/7, 2016 at 14:24 Comment(0)
P
-1

function isDeepEqual(obj1, obj2, testPrototypes = false) {
  if (obj1 === obj2) {
    return true
  }

  if (typeof obj1 === "function" && typeof obj2 === "function") {
    return obj1.toString() === obj2.toString()
  }

  if (obj1 instanceof Date && obj2 instanceof Date) {
    return obj1.getTime() === obj2.getTime()
  }

  if (
    Object.prototype.toString.call(obj1) !==
      Object.prototype.toString.call(obj2) ||
    typeof obj1 !== "object"
  ) {
    return false
  }

  const prototypesAreEqual = testPrototypes
    ? isDeepEqual(
        Object.getPrototypeOf(obj1),
        Object.getPrototypeOf(obj2),
        true
      )
    : true

  const obj1Props = Object.getOwnPropertyNames(obj1)
  const obj2Props = Object.getOwnPropertyNames(obj2)

  return (
    obj1Props.length === obj2Props.length &&
    prototypesAreEqual &&
    obj1Props.every(prop => isDeepEqual(obj1[prop], obj2[prop]))
  )
}

console.log(isDeepEqual({key: 'one'}, {key: 'first'}))
console.log(isDeepEqual({key: 'one'}, {key: 'one'}))
Parlous answered 26/4, 2019 at 20:32 Comment(0)
M
-1

This is a classic javascript question! I created a method to check deep object equality with the feature of being able to select properties to ignore from comparison. Arguments are the two objects to compare, plus, an optional array of stringified property-to-ignore relative path.

function isObjectEqual( o1, o2, ignorePropsArr=[]) {
    // Deep Clone objects
    let _obj1 = JSON.parse(JSON.stringify(o1)),
        _obj2 = JSON.parse(JSON.stringify(o2));
    // Remove props to ignore
    ignorePropsArr.map( p => { 
        eval('_obj1.'+p+' = _obj2.'+p+' = "IGNORED"');
    });
    // compare as strings
    let s1 = JSON.stringify(_obj1),
        s2 = JSON.stringify(_obj2);
    // return [s1==s2,s1,s2];
    return s1==s2;
}

// Objects 0 and 1 are exact equals
obj0 = { price: 66544.10, RSIs: [0.000432334, 0.00046531], candles: {A: 543, B: 321, C: 4322}}
obj1 = { price: 66544.10, RSIs: [0.000432334, 0.00046531], candles: {A: 543, B: 321, C: 4322}}
obj2 = { price: 66544.12, RSIs: [0.000432334, 0.00046531], candles: {A: 543, B: 321, C: 4322}}
obj3 = { price: 66544.13, RSIs: [0.000432334, 0.00046531], candles: {A: 541, B: 321, C: 4322}}
obj4 = { price: 66544.14, RSIs: [0.000432334, 0.00046530], candles: {A: 543, B: 321, C: 4322}}

isObjectEqual(obj0,obj1) // true
isObjectEqual(obj0,obj2) // false
isObjectEqual(obj0,obj2,['price']) // true
isObjectEqual(obj0,obj3,['price']) // false
isObjectEqual(obj0,obj3,['price','candles.A']) // true
isObjectEqual(obj0,obj4,['price','RSIs[1]'])   // true
Matronymic answered 4/9, 2019 at 11:17 Comment(1)
ATTENTION: just to clarify, this code WILL suffer if properties are in different order. I used a model to create the objects so in my case that was not an issueMatronymic
R
-1
const one={name:'mohit' , age:30};
//const two ={name:'mohit',age:30};
const two ={age:30,name:'mohit'};

function isEquivalent(a, b) {
// Create arrays of property names
var aProps = Object.getOwnPropertyNames(a);
var bProps = Object.getOwnPropertyNames(b);



// If number of properties is different,
// objects are not equivalent
if (aProps.length != bProps.length) {
    return false;
}

for (var i = 0; i < aProps.length; i++) {
    var propName = aProps[i];

    // If values of same property are not equal,
    // objects are not equivalent
    if (a[propName] !== b[propName]) {
        return false;
    }
}

// If we made it this far, objects
// are considered equivalent
return true;
}

console.log(isEquivalent(one,two))
Reeding answered 4/10, 2019 at 12:27 Comment(1)
you could at least credit the source adripofjavascript.com/blog/drips/…Crotchety
P
-1

Though there are so many answers to this question already. My attempt is just to provide one more way of implementing this:

const primitveDataTypes = ['number', 'boolean', 'string', 'undefined'];
const isDateOrRegExp = (value) => value instanceof Date || value instanceof RegExp;
const compare = (first, second) => {
    let agg = true;
    if(typeof first === typeof second && primitveDataTypes.indexOf(typeof first) !== -1 && first !== second){
        agg =  false;
    }
    // adding support for Date and RegExp.
    else if(isDateOrRegExp(first) || isDateOrRegExp(second)){
        if(first.toString() !== second.toString()){
            agg = false;
        }
    }
    else {
        if(Array.isArray(first) && Array.isArray(second)){
        if(first.length === second.length){
             for(let i = 0; i < first.length; i++){
                if(typeof first[i] === 'object' && typeof second[i] === 'object'){
                    agg = compare(first[i], second[i]);
                }
                else if(first[i] !== second[i]){
                    agg = false;
                }
            }
        } else {
            agg = false;
        }
    } else {
        const firstKeys = Object.keys(first);
        const secondKeys = Object.keys(second);
        if(firstKeys.length !== secondKeys.length){
            agg = false;
        }
        for(let j = 0 ; j < firstKeys.length; j++){
            if(firstKeys[j] !== secondKeys[j]){
                agg = false;
            }
            if(first[firstKeys[j]] && second[secondKeys[j]] && typeof first[firstKeys[j]] === 'object' && typeof second[secondKeys[j]] === 'object'){
                agg = compare(first[firstKeys[j]], second[secondKeys[j]]);
             } 
             else if(first[firstKeys[j]] !== second[secondKeys[j]]){
                agg = false;
             } 
        }
    }
    }
    return agg;
}


console.log('result', compare({a: 1, b: { c: [4, {d:5}, {e:6}]}, r: null}, {a: 1, b: { c: [4, {d:5}, {e:6}]}, r: 'ffd'}));  //returns false.
Phototype answered 25/11, 2019 at 5:46 Comment(0)
A
-1

This one will check equality for objects

function equalObjects(myObj1, myObj2){

    let firstScore = 0;
    let secondScore = 0;
    let index = 0;

    let proprtiesArray = [];
    let valuesArray = [];


    let firstLength = 0;
    let secondLength = 0;


    for (const key in myObj1) {
        if (myObj1.hasOwnProperty(key)) {
            firstLength += 1;
            proprtiesArray.push(key);
            valuesArray.push(myObj1[key]);
            firstScore += 1;
        }
    }

    for (const key in myObj2) {
        if (myObj2.hasOwnProperty(key)) {
            secondLength += 1;
            if (valuesArray[index] === myObj2[key] &&
                proprtiesArray[index] === key) {
              secondScore +=1;
            }
            //console.log(myObj[key]);

            index += 1;
        }

    }

    if (secondScore == firstScore && firstLength === secondLength) {
       console.log("true", "equal objects");
       return true;
    } else {
       console.log("false", "not equal objects");
       return false;
    }
}

equalObjects({'firstName': 'Ada', 'lastName': 'Lovelace'}, {'firstName': 'Ada', 'lastName': 'Lovelace'});

equalObjects({'firstName': 'Ada', 'lastName': 'Lovelace'}, {'firstName': 'Ada', 'lastName1': 'Lovelace'});

equalObjects({'firstName': 'Ada', 'lastName': 'Lovelace'}, {'firstName': 'Ada', 'lastName': 'Lovelace', 'missing': false});
Antiquary answered 28/7, 2021 at 16:53 Comment(2)
Does not take into account arrays or nested propertiesCharbonnier
I did not made it for account arrays or nested properties It made to check equality between direct 2 objectsAntiquary
P
-1

Affiliation: I am the author of this lib.

See https://eggachecat.github.io/jycm-json-diff-viewer/ for a live demo!

Now it has a JS-native implementation.

It supports configable comapration. You can configure if a list of a path is a ordered list or a set etc.

In the web demo, you'll find functionalities like ordered and unordered array comparisons, and ordered array comparison based on a specific field, all of which can be tightly integrated with business logic through simple configurations. Moreover, it supports custom business algorithms (for example, comparing whether two URLs lead to identical files).

Pressey answered 21/1 at 12:20 Comment(0)
E
-2
function isEqual(obj1, obj2){
    type1 = typeof(obj1);
    type2 = typeof(obj2);
    if(type1===type2){
        switch (type1){
            case "object": return JSON.stringify(obj1)===JSON.stringify(obj2);
            case "function": return eval(obj1).toString()===eval(obj2).toString();
            default: return obj1==obj2;
        }
    }
    return false;
}//have not tried but should work.
Epiclesis answered 19/4, 2013 at 5:32 Comment(1)
Where is it written that JSON.stringify will always list keys in the same order (particularly if they weren't added in the same order to obj1 and obj2).Anyhow
R
-2

Here is generic equality checker function that receives array of elements as input and compare them to each other. Works with all types of elements.

const isEqual = function(inputs = []) {
  // Checks an element if js object.
  const isObject = function(data) {
    return Object.prototype.toString.call(data) === '[object Object]';
  };
  // Sorts given object by its keys.
  const sortObjectByKey = function(obj) {
    const self = this;
    if (!obj) return {};
    return Object.keys(obj).sort().reduce((initialVal, item) => {
      initialVal[item] = !Array.isArray(obj[item]) &&
        typeof obj[item] === 'object'
        ? self.objectByKey(obj[item])
        : obj[item];
      return initialVal;
    }, {});
  };

  // Checks equality of all elements in the input against each other. Returns true | false
  return (
    inputs
      .map(
        input =>
          typeof input == 'undefined'
            ? ''
            : isObject(input)
                ? JSON.stringify(sortObjectByKey(input))
                : JSON.stringify(input)
      )
      .reduce(
        (prevValue, input) =>
          prevValue === '' || prevValue === input ? input : false,
        ''
      ) !== false
  );
};

// Tests (Made with Jest test framework.)
test('String equality check', () => {
  expect(isEqual(['murat'])).toEqual(true);
  expect(isEqual(['murat', 'john', 'doe'])).toEqual(false);
  expect(isEqual(['murat', 'murat', 'murat'])).toEqual(true);
});

test('Float equality check', () => {
  expect(isEqual([7.89, 3.45])).toEqual(false);
  expect(isEqual([7, 7.50])).toEqual(false);
  expect(isEqual([7.50, 7.50])).toEqual(true);
  expect(isEqual([7, 7])).toEqual(true);
  expect(isEqual([0.34, 0.33])).toEqual(false);
  expect(isEqual([0.33, 0.33])).toEqual(true);
});

test('Array equality check', () => {
  expect(isEqual([[1, 2, 3], [1, 2, 3]])).toEqual(true);
  expect(isEqual([[1, 3], [1, 2, 3]])).toEqual(false);
  expect(isEqual([['murat', 18], ['murat', 18]])).toEqual(true);
});

test('Object equality check', () => {
  let obj1 = {
    name: 'murat',
    age: 18
  };
  let obj2 = {
    name: 'murat',
    age: 18
  };
  let obj3 = {
    age: 18,
    name: 'murat'
  };
  let obj4 = {
    name: 'murat',
    age: 18,
    occupation: 'nothing'
  };
  expect(isEqual([obj1, obj2])).toEqual(true);
  expect(isEqual([obj1, obj2, obj3])).toEqual(true);
  expect(isEqual([obj1, obj2, obj3, obj4])).toEqual(false);
});

test('Weird equality checks', () => {
  expect(isEqual(['', {}])).toEqual(false);
  expect(isEqual([0, '0'])).toEqual(false);
});

There is also a gist

Raspberry answered 3/5, 2017 at 12:28 Comment(0)
S
-3

Here's a pretty clean CoffeeScript version of how you could do this:

Object::equals = (other) ->
  typeOf = Object::toString

  return false if typeOf.call(this) isnt typeOf.call(other)
  return `this == other` unless typeOf.call(other) is '[object Object]' or
                                typeOf.call(other) is '[object Array]'

  (return false unless this[key].equals other[key]) for key, value of this
  (return false if typeof this[key] is 'undefined') for key of other

  true

Here are the tests:

  describe "equals", ->

    it "should consider two numbers to be equal", ->
      assert 5.equals(5)

    it "should consider two empty objects to be equal", ->
      assert {}.equals({})

    it "should consider two objects with one key to be equal", ->
      assert {a: "banana"}.equals {a: "banana"}

    it "should consider two objects with keys in different orders to be equal", ->
      assert {a: "banana", kendall: "garrus"}.equals {kendall: "garrus", a: "banana"}

    it "should consider two objects with nested objects to be equal", ->
      assert {a: {fruit: "banana"}}.equals {a: {fruit: "banana"}}

    it "should consider two objects with nested objects that are jumbled to be equal", ->
      assert {a: {a: "banana", kendall: "garrus"}}.equals {a: {kendall: "garrus", a: "banana"}}

    it "should consider two objects with arrays as values to be equal", ->
      assert {a: ["apple", "banana"]}.equals {a: ["apple", "banana"]}



    it "should not consider an object to be equal to null", ->
      assert !({a: "banana"}.equals null)

    it "should not consider two objects with different keys to be equal", ->
      assert !({a: "banana"}.equals {})

    it "should not consider two objects with different values to be equal", ->
      assert !({a: "banana"}.equals {a: "grapefruit"})
Serrate answered 28/5, 2013 at 15:28 Comment(2)
what if someone overwrites the toString method?Derekderelict
Lol, well I guess you may get unexpected results depending on what you did to override it.Serrate

© 2022 - 2024 — McMap. All rights reserved.