When working with JavaScript, you will often need to compare objects, whether it's to check if two objects are equal, to find differences, or to merge data. However, comparing objects in JavaScript isn’t as straightforward as comparing primitive values like numbers or strings.
In this article, we’ll dive into how to compare objects in JavaScript, covering various methods and their use cases.
Why Object Comparison is Tricky
Objects in JavaScript are compared by reference, not by value. This means that if two different objects contain the exact same properties and values, JavaScript will still treat them as unequal unless they reference the same object in memory.
Example:
const obj1 = { name: 'Alice' };
const obj2 = { name: 'Alice' };
console.log(obj1 === obj2); // false
Even though obj1
and obj2
have the same properties, they are different objects in memory, so the comparison returns false
.
1. Comparing Object References
The most basic way to compare objects in JavaScript is by checking their reference:
const obj1 = { name: 'Alice' };
const obj2 = obj1;
console.log(obj1 === obj2); // true
Here, both obj1
and obj2
point to the same object in memory, so the comparison returns true
. However, this method only checks if both variables point to the same object, not if their contents are the same.
2. Shallow Comparison of Object Properties
A shallow comparison checks if the top-level properties of two objects are identical, but does not check deeply nested properties. This is useful if you only need to compare the object at one level.
Using JSON.stringify()
One common way to perform a shallow comparison is to convert the objects to JSON strings and compare those:
const obj1 = { name: 'Alice', age: 25 };
const obj2 = { name: 'Alice', age: 25 };
console.log(JSON.stringify(obj1) === JSON.stringify(obj2)); // true
While this works well for simple objects, it has limitations:
- It doesn't work with non-enumerable properties or functions.
- It doesn't handle object properties that are in a different order but have the same values.
- It can't handle cyclic references (where an object references itself).
Using Object.keys()
Another method is to manually check the object's keys and values:
function shallowEqual(obj1, obj2) {
const keys1 = Object.keys(obj1);
const keys2 = Object.keys(obj2);
if (keys1.length !== keys2.length) return false;
for (let key of keys1) {
if (obj1[key] !== obj2[key]) return false;
}
return true;
}
const obj1 = { name: 'Alice', age: 25 };
const obj2 = { name: 'Alice', age: 25 };
console.log(shallowEqual(obj1, obj2)); // true
This approach compares properties at the first level but doesn’t check nested objects.
3. Deep Comparison of Objects
A deep comparison checks all nested properties and sub-objects to ensure that two objects are structurally identical.
Recursive Comparison
One way to do a deep comparison is to write a recursive function that checks every property of the objects:
function deepEqual(obj1, obj2) {
if (obj1 === obj2) return true;
if (typeof obj1 !== 'object' || typeof obj2 !== 'object' || obj1 === null || obj2 === null) {
return false;
}
const keys1 = Object.keys(obj1);
const keys2 = Object.keys(obj2);
if (keys1.length !== keys2.length) return false;
for (let key of keys1) {
if (!deepEqual(obj1[key], obj2[key])) {
return false;
}
}
return true;
}
const obj1 = { name: 'Alice', age: { years: 25 } };
const obj2 = { name: 'Alice', age: { years: 25 } };
console.log(deepEqual(obj1, obj2)); // true
This function checks if:
- Both objects are equal by reference.
- Both objects are non-null and of type
object
. - Every key and its value are recursively checked for equality.
Using Lodash
For a robust solution, you can use a library like Lodash, which provides a built-in deep comparison function, isEqual
.
import _ from 'lodash';
const obj1 = { name: 'Alice', age: { years: 25 } };
const obj2 = { name: 'Alice', age: { years: 25 } };
console.log(_.isEqual(obj1, obj2)); // true
Lodash’s isEqual handles a wide range of edge cases, such as comparing arrays, nested objects, and even functions.
4. Comparing Arrays of Objects
Sometimes you need to compare arrays of objects. You can use the deep comparison approach with arrays by extending it to check every object in the array:
function deepEqualArray(arr1, arr2) {
if (arr1.length !== arr2.length) return false;
for (let i = 0; i < arr1.length; i++) {
if (!deepEqual(arr1[i], arr2[i])) return false;
}
return true;
}
const arr1 = [{ name: 'Alice' }, { name: 'Bob' }];
const arr2 = [{ name: 'Alice' }, { name: 'Bob' }];
console.log(deepEqualArray(arr1, arr2)); // true
5. Performance Considerations
When comparing objects, particularly deeply nested ones, performance can become an issue. Recursive deep comparisons can be slow for large or complex objects.
- For small objects, JSON.stringify() is simple and quick.
- For large or nested objects, writing or using a well-optimized deep comparison function (like Lodash’s isEqual) is better.
- In performance-critical code, you may also consider caching results or using simpler reference-based comparisons where possible.
Conclusion
Comparing objects in JavaScript requires a bit more work than comparing primitives, but understanding when to use shallow comparison versus deep comparison is key. Whether you’re comparing objects by reference, converting to JSON, or using a recursive deep comparison, choosing the right method will depend on your specific use case.
For most everyday scenarios, a library like Lodash will save time and handle edge cases for you. However, understanding how object comparison works under the hood is essential for writing effective, bug-free JavaScript.
Happy coding!