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

This is similar to this question, but requires a more academic answer:

http://stackoverflow.com/questions/194846/is-there-any-kind-of-hashcode-function-in-javascript

The scenario above demonstrates why it would be necessary to have one, and I'm wondering if there is any equivalent solution.

share|improve this question
You can undo edits to your posts with the rollback button. – Account deleted Oct 14 '08 at 14:17
1  
possible duplicate of Object comparison in JavaScript – Saul Aug 29 '11 at 10:36
14  
@Saul: Before you suggest to close a question as duplicate, you should have a look which one came first ;) – Felix Kling Jan 2 '12 at 10:13

13 Answers

up vote 24 down vote accepted

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 at a value level they are equal. However it is upto the specific type to define how an Equals method should be implemented. An iterative comparision of attributes that have primitive values may not be enough, there may well be attributes which are not to be considered part of the object value. E.g.,

 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 case 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, 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?

share|improve this answer

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

http://lodash.com/docs#isEqual

_.isEqual(object, other);

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

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

share|improve this answer
3  
Underscore's isEqual function is very nice (but you do have to pull in their library to use it - about 3K gzipped). – mckoss Aug 28 '10 at 1:16
2  
That's perfect! As a Haskeller, I was missing all sorts of functions in Node.js. – mcandre Mar 14 '11 at 0:32
3  
if you look at what else underscore gives you, you won't regret pulling it in – PandaWood Jun 12 '12 at 6:01
Thanks, this made me look into Underscore, it's a substitute for my shims! And there's no way I could make all these functions so well-done by myself. – Camilo Martin Jun 12 '12 at 16:53
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. – Dale Anderson Sep 7 '12 at 10:15
show 4 more comments

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
share|improve this answer
2  
Or simply return other.rank == this.rank && other.suit == this.suit; – graham.reeds Mar 27 '10 at 11:33
If the object(s) can be converted to a JSON string, then it makes an equals() function simple. – scotts Nov 14 '12 at 18:28
1  
@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. – Daniel X Moore Nov 14 '12 at 19:32

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));
share|improve this answer
29  
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 ? – Guido García Oct 14 '08 at 15:00
7  
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. – Joel Anair Oct 14 '08 at 18:47
4  
Note that most encoders and stringifiers ignore functions and convert nonfinite numbers, like NaN, to null. – Stephen Belanger Apr 8 '11 at 23:20
3  
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. – Juzer Ali Mar 26 '12 at 7:34
2  
Seems interesting at first, but as everyone has pointed out, it can't be really trusted. – fritzfromlondon Apr 21 '12 at 23:13
show 2 more comments

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.

share|improve this answer

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.

share|improve this answer
1  
I don't think order preservation is guaranteed across browsers/engines. – Jo Liss Mar 7 at 17:46
@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. – Ates Goral Mar 7 at 19:58
Sure, here's some pointers: ECMAScript spec says object is "unordered"; and this answer for actual diverging behavior on current browsers. – Jo Liss Mar 7 at 23:43
@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. – Ates Goral Mar 8 at 0:13
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. – Jo Liss Mar 8 at 4:48

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.

share|improve this answer

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;
}
share|improve this answer
1  
I ended up using a variation of this. Thanks for the idea of counting members! – NateS Aug 26 '10 at 21:42

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' });
share|improve this answer

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.

share|improve this answer

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

share|improve this answer
2  
"bad hashing methods can lead to collisions" => ALL hashing methods can lead to collisions – Joe Oct 14 '08 at 13:59
Yes, thanks for the correction; I edited to try and clarify. – FOR Oct 14 '08 at 14:06

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.

share|improve this answer

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.

share|improve this answer

Your Answer

 
or
required, but never shown
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.