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 User
s, you can compare their emailAddress
field.
It's still not a perfect one, but the benefits over solution #2 are:
- It's predictable, and it's less likely to crash.
- 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.
a.hashCode() == b.hashCode()
does not imply thata
is equal tob
. It's a necessary condition, not a sufficient one. – Houselightsperson = { 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. – Niblickdeep-equal
NPM package – Bing#{ x: 5, y: 7 } === #{ x: 5, y: 7 }
,#[ 4, #{ a: "hello", b: "world" }, 6, 10 ] === #[ 4, #{ b: "world", a: "hello" }, 6, 10 ]
. – TephriteassertEqual
. 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