How Lodash pullAll Handles Object References

This article examines how the Lodash utility method _.pullAll handles arrays containing object references. You will learn the specific equality algorithm Lodash uses, why it treats multiple instances of the same object reference identically, and how it differentiates between identical memory references and structurally identical objects.

The Underlying Equality Check: SameValueZero

_.pullAll modifies an existing array by removing all specified values. When comparing elements from the source array against the values to remove, Lodash internally uses the SameValueZero comparison algorithm.

In JavaScript, SameValueZero behaves identically to the strict equality operator (===), with the exception that it considers NaN to be equal to NaN. When applied to JavaScript objects, this algorithm compares memory addresses rather than the underlying properties or contents of the objects.

Multiple Identical Object References

Because _.pullAll relies on SameValueZero, it does not differentiate between multiple identical object references within the same array.

If an array contains several elements pointing to the same memory reference, _.pullAll treats every instance identically:

const sharedObject = { id: 1 };
const array = [sharedObject, { id: 2 }, sharedObject];

// Pulling sharedObject from the array
_.pullAll(array, [sharedObject]);

console.log(array);
// Output: [{ id: 2 }]

During the iteration process, Lodash evaluates each element:

  1. array[0] === sharedObject resolves to true, so the element is removed.
  2. array[1] === sharedObject resolves to false, so the element is kept.
  3. array[2] === sharedObject resolves to true, so the element is removed.

Every occurrence of the target reference is pulled from the array because all instances point to the exact same location in memory.

Reference Identity vs. Structural Equality

A common source of confusion occurs when objects have identical properties but different references. _.pullAll distinguishes between objects strictly by their reference identity:

const objA = { id: 1 };
const objB = { id: 1 };
const array = [objA];

_.pullAll(array, [objB]);

console.log(array);
// Output: [{ id: 1 }] (objA remains untouched)

Even though objA and objB share the exact same keys and values, they represent distinct objects in memory (objA === objB evaluates to false). Consequently, _.pullAll recognizes them as different entities and leaves objA inside the array.

Pulling by Deep Equality

If the goal is to differentiate or match objects based on their structural content rather than their exact reference, _.pullAll is not the appropriate tool. Lodash provides _.pullAllWith, which accepts a custom comparator function such as _.isEqual:

const array = [{ id: 1 }, { id: 2 }];

// Removes objects by matching structure and values
_.pullAllWith(array, [{ id: 1 }], _.isEqual);

console.log(array);
// Output: [{ id: 2 }]

While _.pullAllWith performs deep property comparisons to identify matching values across separate objects, standard _.pullAll strictly targets identical memory references, purging all occurrences of that reference across the array.