Deep Equality
Write deepEqual(a, b) returning whether the two values are structurally equal.
Two objects are equal when they have the same keys and every corresponding value is deeply equal; key order does not matter. Two arrays are equal when they have the same length and equal values in the same order. Primitives compare with ===, except that NaN equals NaN.
Examples
deepEqual({ x: [1, 2] }, { x: [1, 2] });trueConstraints
- Values contain only objects, arrays, numbers, strings, booleans and null
Notes
- An array is never equal to an object, even one with matching numeric keys.
- `typeof null` is "object", so it needs its own guard.
Hints
Deep Equality (reference solution)
One way to do it. The Editorial tab walks through why it is written this way.
Solution
1function deepEqual(a, b) {
2 if (a === b) return true;
3 if (typeof a === 'number' && typeof b === 'number' && Number.isNaN(a) && Number.isNaN(b)) return true;
4 if (typeof a !== 'object' || typeof b !== 'object' || a === null || b === null) return false;
5 if (Array.isArray(a) !== Array.isArray(b)) return false;
6 const ka = Object.keys(a);
7 const kb = Object.keys(b);
8 if (ka.length !== kb.length) return false;
9 return ka.every((k) => Object.prototype.hasOwnProperty.call(b, k) && deepEqual(a[k], b[k]));
10}Editorial: Deep Equality
Structural, not referential
{a:1} === {a:1} is false. Two objects are the same only when they are the same object, which is why comparing state or props by === catches nothing when the contents changed in place.
Approach
Recurse, with the cheap answers first.
Implementation
function deepEqual(a, b) { if (a === b) return true; if (typeof a === 'number' && typeof b === 'number' && Number.isNaN(a) && Number.isNaN(b)) return true; if (typeof a !== 'object' || typeof b !== 'object' || a === null || b === null) return false; if (Array.isArray(a) !== Array.isArray(b)) return false; const ka = Object.keys(a); const kb = Object.keys(b); if (ka.length !== kb.length) return false; return ka.every((k) => Object.prototype.hasOwnProperty.call(b, k) && deepEqual(a[k], b[k])); }
Worth knowing
Three cases catch people. typeof null is "object", so null needs its own guard or you dereference it. Comparing key counts before walking is what makes {a:1} and {a:1,b:2} unequal rather than equal-on-what-they-share. And Array.isArray(a) !== Array.isArray(b) is what stops [1] matching {0:1}, which have identical keys.
NaN === NaN is false, so equality of two NaNs is a decision you make explicitly. Object.is gets it right and gets +0/-0 wrong for this purpose.