1035

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.

15
  • 5
    Also look into this question stackoverflow.com/q/1068834/1671639 Commented May 6, 2014 at 9:48
  • 58
    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. Commented Sep 24, 2014 at 13:34
  • 7
    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?" Commented Feb 24, 2017 at 17:50
  • 14
    @th317erd can you please explain yourself?... Commented May 14, 2018 at 12:44
  • 3
    @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. Commented May 14, 2018 at 21:01

77 Answers 77

725

For transparency, Lodash was still being regularly updated when this answer was edited to recommend it. Lodash has not had an update since version 4.0.0 was released in January 2016.


Give the Lodash library 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.

Sign up to request clarification or add additional context in comments.

14 Comments

Underscore's isEqual function is very nice (but you do have to pull in their library to use it - about 3K gzipped).
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.
@mckoss you can use the standalone module if you don't want the entire library npmjs.com/package/lodash.isequal
Also consider the deep-equal package, which even works for arrays of objects.
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.
|
230

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?

1 Comment

This 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.
206

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

6 Comments

If the object(s) can be converted to a JSON string, then it makes an equals() function simple.
@scotts 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.
Shouldn't we compare the data type as well?! return other.rank === this.rank && other.suit === this.suit;
@devsathish 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.
@scotts 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}
|
131

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);
}

17 Comments

You could replace reduce with every to simplify.
@nonkertompf sure he could: Object.keys(x).every(key => deepEqual(x[key], y[key])).
This fails when you are comparing two dates
deepEqual({}, []) returns true
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))
|
90

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 } }));

14 Comments

objectEquals([1,2,undefined],[1,2]) returns true
objectEquals([1,2,3],{0:1,1:2,2:3}) also returns true -- e.g. there is no type checking, only key/value checking.
objectEquals(new Date(1234),1234) returns true
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'.
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.
|
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>

Comments

69

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.

12 Comments

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 ?
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.
Note that most encoders and stringifiers ignore functions and convert nonfinite numbers, like NaN, to null.
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.
This could work with an alternate JSON stringifier, one that sorts object keys consistently.
|
54

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

1 Comment

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 deal
32

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.

5 Comments

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) = false
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).
@DaveF in that case, perhaps Map would be more appropriate than a wild object, semantically.
@Aidin My point was that sometimes all properties and values need to be checked. The example is not important.
Please do not post new answers that just repeat (even a collection of) existing answers. if you want to highlight a particularly useful aspect of an existing answer, do so via a comment (and an upvote), not via an answer. Answers should be reserved for novel, distinct solutions to the problem posed in the question, not a ranking explanation of some of your own favorite existing answers.
31

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;
};

3 Comments

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;
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') {.
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.
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.

2 Comments

It is a long method but it completely tests the objects without making any assumptions on the order of the properties in each object.
does not work if a property is an array of other objects
24
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.

3 Comments

This won't work if object2 has additional keys which object1 doesn't include.
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 though
@RamKumar just add in the condition var result = Object.keys(object1).every((key) =>(object1[key] === object2[key] && object1.hasOwnProperty(key) && object2.hasOwnProperty(key))
23

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.

7 Comments

I don't think order preservation is guaranteed across browsers/engines.
@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.
Sure, here's some pointers: ECMAScript spec says object is "unordered"; and this answer for actual diverging behavior on current browsers.
@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.
@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...
|
23

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

3 Comments

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.)
yup you are right ... for the object, you have to sort recursively for both side objects
This does not work for objects with circular references
18

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}}.

1 Comment

good idea (in my case the objects to be compared a just key/value-pairs, no Special things)
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

1 Comment

Does not work if the order of the object keys has changed.
13

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.

2 Comments

small correction : before going through each props in a and b add this check if(Object.getOwnPropertyNames(a).length !== Object.getOwnPropertyNames(b).length ) return false
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 help
7

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]

5 Comments

This is a fully functional answer, thanks @Adriano Spadoni. Do you know how can I get the key/attribute that was modified? Thanks,
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.
Do not ever use JSON.stringify to compare json objects. Order of the keys is not expected to be the same.
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.
@digital, to get the difference would need a RegExp instead of the "===", it is doable.
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}}));

Comments

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;

        }
    )
}

Comments

5

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

Comments

5

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 :).

Comments

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.

Comments

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.

Comments

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));

2 Comments

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.
What is the properties are in different order ??? Nope not a good method
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.

Comments

3

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;
});

4 Comments

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;
areEqual({a: 'b'}, {b: 'a'}) gets true then?
Yea, I realized after posting that this "solution" has issues. It needs a bit more work in the sorting algorithm to actually work properly.
@th317erd it's possible to edit your post if you want to improve it.
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

3 Comments

your solution will return true for objects {one: 'two'} and {two: 'one'}.
You are absolutely right. I will edit to fix.
I used a simple JSON.stringify(a) == JSON.stringify(b), knowing that the object keys will always be in the same order. Nice to see that mentioned along with its pitfall.
3

Here is a more generic object comparison function than what has ben posted thus far:

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;
}

1 Comment

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) {...}?
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.

Comments