Deep equality in JavaScript objects
By: Raphael Castro Martin
Tags: Equality, Objects In Javascript, JavaScript

Compare data in Javascript is always a task that involves some more concern than other languages. The reasons are wide, but generally is related to JS be weak-typed.

One of many JS weirdness

A way to avoid the problem above is use the strict equality operator that considers the type of the data you are trying to compare to assert the equality. But the problem continues if you need to do something a little bit more complex.

Comparing objects

When you are comparing objects in JavaScript, actually the interpreter is comparing the instance (the pointer, or whatever you want name it) of the object. This fact means that every time you are using a raw value (not a variable) in a comparison, the result will be false, independently if you are using the regular equality operator or the strict one.

{ name: ‘Raphael’ } == { name: ‘Raphael’ }

// false

{ name: ‘Raphael’ } === { name: ‘Raphael’ }

// false

let myObj = { name: ‘Raphael’ }

myObj == { name: ‘Raphael’ }

// false

myObj === { name: ‘Raphael’ }

// false

But if you compare the current instance of the object, the result is different:

let myObj = { name: ‘Raphael’ }

myObj == myObj

// true

let mySecondObj = myObj // This sets to mySecondObj the same memory address that myObj

mySecondObj == myObj

// true

let myThirdObj = { name: ‘Raphael’ }

myObj == myThirdObj

// false

myObj.name += ‘ Martin’ // Changing myObj, and also mySecondObj

console.log(mySecondObj) // Printing mySecondObj

// {name: "Raphael Martin"}

So, what can you do when you need to check if two different objects have the same properties with the same values? Well, I am sure that there is some npm package just to solve this, or some lodash function that does exactly what you want to do, but I judged that would be nice to create my own.

Creating a function to compare objects

The first thing I thought was: I can iterate the object and compare each property from first object to its correspondent in the second. Something like that:

/**

*

* @param a Object

* @param b Object

* @returns {boolean}

*/

function isEqualObjects (a, b) {

for (const key in a) { // Using for-in in a object you can iterate the properties

const a_value = a[key]

const b_value = b[key]

if (a_value !== b_value) {

return false

}

}

return true

}

isEqualObjects(myObj, myThirdObj)

// true

This simple generic function solves most of the problems in development, and in my opinion it is a better approach than put a new JS library in your package.json, which would require to assure that it is in correct version, still available, receiving commits, respecting secure standards, etc. But this solution has a problem: it doesn't make a deep comparison.

let first = {name: ‘L. Hamilton’, car: {team: ‘Mercedes’, tyre: ‘soft’}}

let second = {name: ‘L. Hamilton’, car: {team: ‘Mercedes’, tyre: ‘soft’}}

isEqualObjects(first, second)

// false

It occurs because when setting the 'car' property of the object with an object value, in fact we are instantiating a new object, creating a new pointer. And when the iteration is in the 'car' property it compares two different instances of an object with the operator !==, that always will be truly. How can we solve this? Recursion!

The beauty of a recursion algorithm

So the approach is simple. We are writing a function that checks if objects are equal. If the property of the two objects is also an object, we use the current function to check their properties inequality, else we use the !== operator. The final code is something like this:

/**

*

* @param a Object

* @param b Object

* @returns {boolean}

*/

function isEqualObjects (a, b) {

// if the number of keys is different, they are different

if (Object.keys(a).length !== Object.keys(b).length) {

return false

}



for (const key in a) {

const a_value = a[key]

const b_value = b[key]

// If the value is an object, check if they're different objects

// If it isn't, uses !== to check

if ((a_value instanceof Object && !isEqualObjects(a_value, b_value)) || (!(a_value instanceof Object) && a_value !== b_value)) {

return false

}

}

return true

}

Now, we can deeply compare the objects:

let first = {

"name": "L. Hamilton",

"car": {

"team": "Mercedes",

"tyre": "soft",

"specs": {

"weight": 733,

"liters": 1.6,

"power": 1000

}

}

}

let second = {

"name": "L. Hamilton",

"car": {

"team": "Mercedes",

"tyre": "soft",

"specs": {

"weight": 733,

"liters": 1.6,

"power": 1000

}

}

}

isEqualObjects(first, second)

// true

If you have any suggestions, comment below!