Shallow Equality
Prompt
Write a JavaScript function shallowEqual(obj1, obj2) that checks if two given objects are shallowly equal.
Playground
Start by comparing the number of keys in both objects. If they have a different number of keys, they can't be equal.
Loop through the keys of the first object. For each key,
check if the second object has the same value at that key
using ===. If any value doesn't match, return false.
If you get through all keys without a mismatch, return
true.
Solution
Explanation
In JavaScript, { a: 1 } === { a: 1 } is false. That surprises a lot of people when they first see it.
The reason is that === doesn't compare what's inside two objects. It compares whether they're the same object in memory. These two objects look identical, but they were created separately, so they live at different addresses in memory. JavaScript sees them as two completely different things.
const obj1 = { name: 'Alice' };
const obj2 = { name: 'Alice' };
obj1 === obj2; // false (different objects, different places in memory)
const obj3 = obj1;
obj1 === obj3; // true (same object, same place in memory)This is the problem shallow equality solves. Instead of asking "are these the same object?", it asks "do these objects have the same stuff inside them?"
What "shallow" means
Shallow equality checks the first level of properties only. It looks at each key in one object, finds the same key in the other, and compares the values using ===.
shallowEqual({ a: 1, b: 2 }, { a: 1, b: 2 }); // true
// key "a": 1 === 1 ✓
// key "b": 2 === 2 ✓But here's where "shallow" matters. If one of those values is itself an object, the comparison uses === on that nested object. And as we just learned, === on two different objects is always false, even if they look the same:
shallowEqual(
{ a: 1, address: { city: 'NY' } },
{ a: 1, address: { city: 'NY' } }
);
// false!
// key "a": 1 === 1 ✓
// key "address": { city: 'NY' } === { city: 'NY' } ✗ (different objects!)The two address objects have the same content, but they're different objects in memory, so === says false. Shallow equality doesn't look inside nested objects. It only checks the surface.
If both properties point to the same nested object (same reference), then it works:
const address = { city: 'NY' };
shallowEqual({ a: 1, address }, { a: 1, address });
// true! Both "address" values point to the exact same objectHow the solution works
The solution does two things:
-
Compare the number of keys. If
obj1has 3 keys andobj2has 2, they can't be equal, so returnfalseright away. -
Loop through every key in
obj1and compare the value at that key using===. If any value doesn't match, returnfalse. If we make it through every key without a mismatch, returntrue.
function shallowEqual(obj1, obj2) {
const keys1 = Object.keys(obj1);
const keys2 = Object.keys(obj2);
if (keys1.length !== keys2.length) return false;
for (const key of keys1) {
if (obj1[key] !== obj2[key]) {
return false;
}
}
return true;
}That's it. No recursion, no special handling. Just "compare key count, then compare each value with ===."
React uses shallow equality internally. When you pass
props to a component wrapped in React.memo, React does a
shallow comparison of the old and new props to decide
whether to re-render. This is why you sometimes see
unexpected re-renders when passing new object or array
literals as props, even if the contents haven't changed.
The object reference is new, so shallow equality says
"these are different" and React re-renders.