JavaScript object comparison

Function for deep comparison of object instances and arrays in JavaScript. It compares all data types except functions, because functions are not data, nor they should contain it. All functions should be unique, two same functions don’t make sense and/or use. This article covers the detailed explanation of an approach to solve the deep object comparison problem in JavaScript.

Introduction

Comparing two objects and two arrays is a different story in JavaScript then comparing two strings, numbers and booleans. Well arrays are in a way objects with ordered and enumerated property names and length property. So two arrays or objects are equal only if they reference the same same instance, that is, the same unique location in memory. Simple types as string, number, boolean, null and undefined can be wrapped in an object but regardless they are compared by value and not by a reference.

var a = { foo:1, bar: 'baz'};
var b = a;
alert(a === b); // true
delete b.bar;
alert(a.bar); //undefined
a = [1,2,3];
a === b //false, because b still references the object defined above, {foo:1}
b = a; //JavaScript garbage collector removes the object when it isnt referenced anymore.
a === b // true

Problem

But if we define two objects (or arrays, but about the arrays there will be more words later) that have the same inner structure, they wont be equal because they are two separate objects in memory with the same structure.

var a = {foo: 1, bar: 'baz'};
var b = {foo: 1, bar: 'baz'};
alert(a === b); // false
delete b.bar;
alert(a.bar); //baz

Solution

So how do we compare them by value?
The easiest possible approach would be to convert or stfingify array or object to sting and compare two strings. But if you remember that objects have unordered property structure, and you iterate them the way they were put into the object, then ‘{a: 1, b:2}’ != ‘{b:2, a:1}’, but in reality these =two are equal objects by structure and value.

Let’s say we have two objects, “a” and “b”. Pass recursively through all of the the properties of “a” and check if they exist in “b”, if there is no matching property in “b”, stop and declare equality false, but if there is compare two properties by value. Recursion here means that if both properties have an object or an array execute the same function passing the two nested objects or array. So this comparison is deep comparison, or a multi level comparison. Now that we compared “a” to “b” and everything passed well, we must not forget to see if “b” has some additional properties that “a” doesn’t have. We only check for additional properties because we already have done the value comparisons. If it stumbles upon an additional property objects are not equal.

Array comparison is a bit easier, if two arrays are not the same length they are not equal. Otherwise pass through each element of the arrays and compare them. If elements are objects or arrays use appropriate recursion, if not equal throw false.

It is important to note that Date objects and RegExp objects have the same problem when it comes to comparing two of them. But their comparison is even easier. To compare two Date objects in JavaScript you can simply convert them to unix timestamp and compare the number. And to compare the RegExp object, we have to convert them to string.

As you can see there are a lot of different functions defined by the type of the objects compared. We can unite them into one function (simplifying the usage) which we can call “equal”, and declare the functions for each type in some namespace, to escape using cases (fast and elegant). First we check if two values passed to “equal” are equal by value and type, if not we see which type are they, if they are not the same type they are not equal, but if they are they might be objects and selecting the correct declared function via type we try to compare them. If comparison fails we return false if it passes we continue. If all is well at the end then two values are equal. Inside the object comparison and array comparison functions we call “equal” function for each pair of values, as you can see in the following code (the previously noted recursion isn’t obvious right away).

Code

Note that “whatis” function for data type detection is described in this post: http://stamat.wordpress.com/2013/04/09/javascript-data-type-detection/

Also note that value comparison at the beginning of the functions in compareObjects and compareArrays is redundant with the one in “equals”, but this is done on purpose so the two functions can be used separately from “equal”.

Conclusion

Now I have seen several solutions on Stack Overflow that use several “for” loops without any concern about the performance, some count the number of properties, some check them for undefined. But in an object you can have a defined property with an undefined value, and many of them seem to forget that. Also prototyping Object type is not recommended and punishable by DEATH! Heh, I’m just kidding. But seriously don’t extend Object type for God’s sake, think about it! And using typeof literal doesn’t fit all the cases (previously noted undefined), or given solutions slip into infinite loop due to cyclic objects…
And why function comparison? It just doesn’t add up… Functions cannot be parsed in JSON, reason: security issues. And you should compare objects that hold data, you will never end up comparing classes and functions. And if you really want to hold function in an object just reference it, it will have more sense… A lot of people who solved this problem one way or another fall back to compare functions as strings, but this will fail cause ‘function(a,b){ return a+b; }’ != ‘finction(a,b){ return b+a; }’, but these functions are the same, and function in the same way. The solution here is to tokenize the function code and then construct functions in the same order, convert them to string and then compare them. But then think about the performance.

I’m not saying that solution suggested is any better, nor I’m trying to compete with anyone, I’m only pointing out at some noticeable issues. I just wrote my “equal” function without looking what others have done, in my style for personal usage and I wanted to share it with the rest of the world. I hope that you will leave me some constructive feedback so I can further improve myself. And i really hope you found this article and code useful.

About these ads

6 comments

  1. Check this out: https://gist.github.com/proof/7f4f655b1f7ce4055d51
    Underscore.js _.isEqual returns true on comparing these objects.
    :)

    1. E bravo, to je jer nisu isti po tipu. A na kraju krajeva ovo je pisano za validaciju JSON objekata, tako da meh, propustio sam… A i izmisljam toplu vodu pisuci nesto poput underscore, dad cem sredim da radi. Jedna pusa za tebe :*

    2. Now it’s fixed and it works smooth like a baby’s ass (no pedo), thanks for the feedback bro! :D

  2. I wanted to share a slightly different consideration when doing object comparison. It comes from the need to do unit tests and immediately be able to spot any differences. For that a string comparison is best. I talk about it more on my blog (http://yagudaev.com/posts/comparing-object-in-javascript).

    Now, most of the time in a big test suite you will get that majority of the tests pass. So combining your approach with mine will bring the most benefit. In other words writing an assertDeepEquals function that first uses your approach to compare the objects. If the objects are not equal, then it will use my approach with converting them to strings so that a string comparison tool can be used to show the differences. That way you are both getting the performance and usability of both.

    1. Hey thanks! :) I was just interested in object comparison so I looked only into that. My first idea was very similar, I reordered the properties just like you, alphabetically and recursive. Then I did a CRC32 of the both objects and compared the numbers. Well I hashed them so i could make a map out of the list of objects and search them very fast. But collision of hashes made me drop that idea.
      The string difference comparison is awesome! Good job! I guess you are using hamming distance, am I right?
      Anyway, once again thanks for a nice comment! :D And awesome work!

  3. […] external functions are orderProperties (self explanatory), and functions described in older posts: JavaScript deep object comparison and JavaScipt data type […]

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

Join 79 other followers

%d bloggers like this: