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:
array[0] === sharedObjectresolves totrue, so the element is removed.array[1] === sharedObjectresolves tofalse, so the element is kept.array[2] === sharedObjectresolves totrue, 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.