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:
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!
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.
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 !==
:
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.
...
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.
...
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:
...
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:
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.
Top comments (7)
Hi,
Thanks for the post, it's really helpful!
I've just reformatted and refactored a bit your code, so now it's following ESLint standards.
Any particular reason why JSON.stringify isn't mentioned here?
In most cases it is pretty useful to use JSON methods, but functions and also React componens won't survive the serialization process using the JSON methods. But still, using the spread method for simple objects and JSON methods for more complex would be fine! My goal was to learn something about recursive functions with this article.
Relevant interesting article: dev.to/bytebodger/cloning-objects-...
Doesn't work with methods :(
You are right! I've updated the code to also check for methods:
if (typeof a[key] === 'function' || typeof b[key] === 'function') {
if (a[key].toString() != b[key].toString()) return false;
}
Thankyou!
i think you can use JSON.stringify the both objects then you can compare it !
the only issue here you need to catch JSON.stringify error !
What if you have objects with different field order? Like
{ foo: 'bar', baz: 42 }
vs{ baz: 42, foo: 'bar' }
. How do you handle that with JSON.stringify?