Object comparison in javascript works a bit differently than primitive value comparison.
Objects are compared by reference while primitives are compared by value.
For example,
const obj1 = { name: 'John', age: 25 };
const obj2 = { name: 'John', age: 25 };
console.log(obj1 === obj2); // false
// primitive value comparison
const a = 10;
const b = 10;
const name = 'Jack';
const surname = 'Jack';
console.log(a === b); // true
console.log(name === surname); // true
Since the above objects are compared by reference which means both are stored at different memory locations.
So there are two different ways to compare objects:
- Shallow Equality
- Deep Equality
Shallow Equality
JSON.stringify()
This is the first method that comes to my mind when looking to compare two objects, though this approach has several limitations. It is useful in cases where the order of the keys of two given objects are the same.
For example,
const person1 = {
name: 'jack dorsey',
founded: 'twitter',
addresses: ['new york', 'boston']
};
const person2 = {
name: 'jack dorsey',
founded: 'twitter',
addresses: ['new york', 'boston']
};
console.log(JSON.stringify(person1) === JSON.stringify(person2)); // true
// order of key changes
const person3 = {
founded: 'twitter',
addresses: ['new york', 'boston'],
name: 'jack dorsey'
};
console.log(JSON.stringify(person3) === JSON.stringify(person2)); // false
Custom isEqual check
function isEqual(obj1, obj2) {
if (Object.keys(obj1).length !== Object.keys(obj2).length) return false;
for (let [key, value] of Object.entries(obj1)) {
if (!obj2[key] || obj2[key] !== value) {
return false;
}
}
return true;
}
console.log(isEqual({
name:'hela',
age: 5000
},
{
name:'hela',
age: 5000
})); // true
console.log(isEqual({
name:'hela',
age: 5000,
power: 90000
},
{
name:'hela',
age: 5000
})); // false
console.log(isEqual({
name:'hela',
age: 5000
},
{
name:'hela',
age: 4000
})); // false
But the above method has a major limitation which is if some key has an object or array as its value then this method breaks.
For example,
console.log(isEqual({
name: 'Shazam',
age: 15,
superpowers: ['flying', 'thundershock']
},
{
name: 'Shazam',
age: 15,
superpowers: ['flying', 'thundershock']
})); // false
Deep Equality
This approach helps solve the limitation faced by above method.
const person1 = {
name: 'Jatin',
age: 25,
address: {
city: 'Mundra',
state: 'Gujarat',
nation: 'India'
}
}
const person2 = {
name: 'Jatin',
age: 25,
address: {
city: 'Mundra',
nation: 'India',
state: 'Gujarat'
}
}
const person3 = {
name: 'Jatin',
age: 25,
address: {
city: 'Baroda',
nation: 'India',
state: 'Gujarat'
}
}
function isDeepEqual(obj1, obj2) {
if (Object.keys(obj1).length !== Object.keys(obj2).length) return false;
for (let [key, value] of Object.entries(obj1)) {
if (!obj2[key]) {
return false;
} else {
if (typeof obj2[key] === 'object' && typeof value === 'object') {
return isDeepEqual(obj2[key], value);
} else if (typeof obj2[key] !== 'object' && typeof value !== 'object') {
if (obj2[key] !== value) {
return false;
}
} else {
return false;
}
}
}
return true;
}
isDeepEqual(person1, person2); // true
isDeepEqual(person2, person3); // false
Parting Notes
This problem is covered well by libraries like Lodash, Underscore. This problem is a favorite interview question for entry level javascript engineers. Thanks for taking the time to read this. I'd love some feedback. All the best for your upcoming interview.
Top comments (3)
Good article . Although, your isDeepEqual() function is slightly incorrect.
when we check for a nested property the function returns true preemptively without checking further key , value pair.
test case for which it is failing : (returns true although it should return false)
const person1 = {
address: {
city: 'Mundra',
state: 'Gujarat',
nation: 'India'
},
name: 'Jatin',
age: 25,
}
const person2 = {
address: {
city: 'Mundra',
nation: 'India',
state: 'Gujarat'
},
name: 'Jatin',
age: 27,
}
nice catch
Thank you. This is helpful and useful.