Deep Equality checking of Objects in Vanilla JavaScript 👨‍👦
Tags: javascript
Published: 2020-04-28 13:27:47

Did you ever found yourself in a situation where you needed to compare two objects to each other with JavaScript? Perhaps you then found out that JavaScript does not offer a native solution for this issue. **In this tutorial we will build own implementation for this!**

You will learn something about:
- Pass by value vs pass by reference
- Object.keys() method
- Creating a recursive function

You could grab the Lodash library and use their `.isEqual` method to do a deep quality check of two objects but it is good practise to create solutions ourselves to practise vanilla JavaScript.

Let's say we have the following objects:

```javascript
const obj1 = { name: 'Peter', stats: { points: 45, isActive: false }};
const obj2 = { name: 'Peter', stats: { points: 45, isActive: false }};

console.log(obj1 === obj2) // returns false
```

These two objects are exactly the same, still JavaScript returns false. Why?

This is because in JavaScript Primitives like strings and numbers are *compared by their value*. Objects on the other hand are *compared by reference*. 

**JavaScript assigns each object you create to its own place in memory. So even if you're objects have exactly the same content, their reference (place in memory) is different!**

![visual](https://blog.penjee.com/wp-content/uploads/2015/02/pass-by-reference-vs-pass-by-value-animation.gif)

Let's start creating our function. We will set up a function called `compareObjects` that takes in two arguments. First we'll check if the two arguments are of the same type and contain the same value.

```javascript
const compareObjects = (a, b) => a === b ? true : false;

const obj1 = { name: 'Peter', stats: { points: 45, isActive: false }};

compareObjects(obj1, obj1) // returns true
```

Next up we'll add the check if the two arguments are actually of the type `object` and also are not `null` values. We want to avoid type conversion so we'll use `!=` instead of `!==`:

```javascript
const compareObjects = (a, b) => {
 if (a === b) return true;
​
 if (typeof a != 'object' || typeof b != 'object' || typeof a == null || typeof b == null) return false;
}
```

Then we'll check the length of the object keys of both objects. If they are not of the same length, we are sure the object are not the same.

```javascript
...
let keysA = Object.keys(a), keysB = Object.keys(b);
 if (keysA.length != keysB.length) return false;
...
```

Next up we'll loop over the keys of the keysA array with an `for of` loop. Use `for of` for arrays and `for in` for objects.

Inside this loop, we'll check if every key exists inside the keysB array. Next to that, we'll compare the values of every key by passing them back into our `compareObjects` function, making our function recursive (calling itself).

As soon as one of our keys of values is not the same, it will stop the loop and the function and return false.

```javascript
...
for (let key of keysA) {
    if (!keysB.includes(key) || !compareObjects(a[key], b[key])) return false;
}
...

```

We also want to check if the methods are the same, we will do this by converting the function to a string on comparing the two values:

```javascript
...
if (typeof a[key] === 'function' || typeof b[key] === 'function') {
   if (a[key].toString() != b[key].toString()) return false;
}
...
```

If the loop checked every key and passed every nested value back into its own function and none returned false, there is only one thing left to do: return true!

The complete function:

```javascript
const compareObjects = (a, b) => {
 if (a === b) return true;

 if (typeof a != 'object' || typeof b != 'object' || a == null || b == null) return false;

 let keysA = Object.keys(a), keysB = Object.keys(b);

 if (keysA.length != keysB.length) return false;

 for (let key of keysA) {
   if (!keysB.includes(key)) return false;

   if (typeof a[key] === 'function' || typeof b[key] === 'function') {
     if (a[key].toString() != b[key].toString()) return false;
   } else {
     if (!compareObjects(a[key], b[key])) return false;
   }
 }

 return true;
}
```

Thanks for following this tutorial, make sure to follow me for more! 🧠

*Please see the book Eloquent JavaScript for further reference.*