Easy

Shallow Equality

Prompt

Write a JavaScript function shallowEqual(obj1, obj2) that checks if two given objects are shallowly equal.

Playground

Hint 1

Start by comparing the number of keys in both objects. If they have a different number of keys, they can't be equal.

Hint 2

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 object

How the solution works

The solution does two things:

  1. Compare the number of keys. If obj1 has 3 keys and obj2 has 2, they can't be equal, so return false right away.

  2. Loop through every key in obj1 and compare the value at that key using ===. If any value doesn't match, return false. If we make it through every key without a mismatch, return true.

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.