Deep equality is a concept in JavaScript that involves comparing complex data structures, such as arrays and objects, to determine if their contents are equal. It goes beyond simple value comparisons and checks whether the elements within nested structures are also equal.
To achieve deep equality, you need to recursively traverse the data structures and compare their elements. Deep equality is useful when you want to ensure that the entire structure, including nested objects and arrays, is identical.
Deep equality is different from following equality checks in JavaScript:
==
) checks if two values are equal after type coercion.===
) checks if two values are equal and requires both the value and data type to be identical. Unlike loose equality, strict equality does not perform type coercion, making it a safer choice for most value comparisons.Object.is()
is similar to ===
in most respects, but with two notable differences related to NaN
and -0
.// Comparing NaN
console.log(NaN == NaN); // false
console.log(NaN === NaN); // false
console.log(Object.is(NaN, NaN)); // true
// Comparing -0 and 0
console.log(-0 == 0); // true
console.log(-0 === 0); // true
console.log(Object.is(-0, 0)); // false
When you compare reference types (arrays, objects, dates, etc.) using ==
, ===
, or Object.is()
, you’re comparing the memory addresses (references) of the objects, not the contents of the objects themselves.
If you want to compare the contents of objects or arrays, you would need to implement a custom deep equality function or use a library like Lodash’s _.isEqual()
to perform such comparisons.
_.isEqual()
of LodashTo use Lodash to perform deep equality, you can use the _.isEqual()
method, which performs a deep comparison between two values to determine if they are equivalent. This method supports comparing arrays, objects, and many other types of values, such as numbers, strings, symbols, etc.
var x = [{a:1, b:2}, {c:3, d:4}];
var y = [{b:2, a:1}, {d:4, c:3}];
_.isEqual(x, y); // => true
If you change the value of any property in either array, the value of d in the second object of y is different from the value of d in the second object of x, it will return false:
var x = [{a:1, b:2}, {c:3, d:4}];
var y = [{b:2, a:1}, {d:5, c:3}];
_.isEqual(x, y); // => false
Using a library like Lodash can save you the effort of writing complex deep equality comparison logic from scratch. It’s a well-tested and reliable solution for comparing complex data structures.
Writing your own comparison function can be useful when you want to have more control over how the objects are compared, or when you don’t want to rely on external libraries or methods. But it is very challenging, as you need to consider various cases and scenarios, such as:
NaN
, -0
, +0
, null
, undefined
, etc.This function can be complex, especially when dealing with nested arrays and objects, as you’ve noticed. If you find it challenging and want a simpler solution, you can consider using an established library like Lodash or a utility function provided by your JavaScript framework or library of choice.
You can use JSON.stringify
as a technique to perform a deep equality check on objects, but it comes with some caveats. Here’s how you can use JSON.stringify
:
function deepEqualWithJSON(obj1, obj2) {
return JSON.stringify(obj1) === JSON.stringify(obj2);
}
// Example usage
const objA = {
name: 'John',
age: 30,
address: {
city: 'New York',
zip: '10001',
},
};
const objB = {
name: 'John',
age: 30,
address: {
city: 'New York',
zip: '10001',
},
};
const objC = {
name: 'Alice',
age: 25,
address: {
city: 'Los Angeles',
zip: '90001',
}
};
console.log(deepEqualWithJSON(objA, objB)); // true (the contents are the same)
console.log(deepEqualWithJSON(objA, objC)); // false (different values)
This approach serializes the objects into JSON strings and then compares the strings for equality. It can work for simple cases, but there are important limitations and considerations:
JSON.stringify
does not preserve functions, so if your objects contain functions, those functions will not be considered in the comparison.JSON.stringify
and JSON.parse
can have performance implications, especially for deeply nested objects or large arrays.deepEqual()
and deepStrictEqual()
in Node.jsIn Node.js, the assert
module provides various functions for performing assertions and tests in your code. Two of the assertion methods in this module are assert.deepEqual()
and assert.deepStrictEqual()
, which are used for deep equality checks.
These methods allow you to compare complex data structures like objects and arrays to ensure that their contents are equal. The difference between them lies in how they handle strict equality:
deepEqual()
performs a deep comparison but allows type coercion, so values that can be implicitly converted are considered equal.deepStrictEqual()
requires that not only the content but also the data types are identical. It does not allow type coercion, so values with different types are considered unequal.Here is an original example for assert.deepStrictEqual()
method in Node.js to test if two objects are deeply equal:
const assert = require('assert');
// Define two objects with different types of values
let obj3 = {
name: 'Alice',
age: '25', // string
hobbies: ['reading', 'writing', 'coding'],
address: {
city: 'New York',
zip: 10001
}
};
let obj4 = {
name: 'Alice',
age: 25, // number
hobbies: ['reading', 'writing', 'coding'],
address: {
city: 'New York',
zip: 10001
}
};
// Use assert.deepStrictEqual to check if the objects are deeply equal
try {
assert.deepStrictEqual(obj3, obj4);
console.log('The objects are deeply equal');
} catch (error) {
console.error(error);
}
// AssertionError [ERR_ASSERTION]: Expected values to be strictly deep-equal
This example will print an error to the console, because the age property of obj3
is not strictly equal to the age property of obj4
.