How Lodash pullAllWith Handles Deep Comparisons

Lodash's _.pullAllWith method removes specified values from an array by filtering out elements that match according to a custom comparator function. Unlike methods that rely on standard strict equality (===), _.pullAllWith enables deep comparisons of complex nested objects and arrays when paired with a deep equality comparator like _.isEqual. This article explains how the method processes these comparisons, how it mutates the source array, and how to implement it correctly.

The Limitation of Reference Equality

Standard JavaScript equality operators and Lodash methods like _.pull or _.pullAll compare objects by reference, not by structural value. Two distinct objects with identical keys and values evaluate as unequal because they occupy different locations in memory:

{ id: 1 } === { id: 1 }; // false

Because of this behavior, standard removal methods fail when you attempt to pull an object out of an array using a separate object instance with the same properties.

Enabling Deep Equality with a Comparator

_.pullAllWith accepts three arguments: the array to modify, the array of values to remove, and a comparator function:

_.pullAllWith(array, values, [comparator]);

The comparator function takes two arguments: comparator(arrVal, othVal). It compares an element from the target array with an element from the values array, returning true if they should be considered a match.

To perform deep comparisons, you supply a deep equality function as the comparator. Lodash provides _.isEqual, which performs an exhaustive structural check across nested objects, arrays, maps, sets, and primitive wrappers:

const _ = require('lodash');

const users = [
  { id: 1, profile: { role: 'admin' } },
  { id: 2, profile: { role: 'editor' } },
  { id: 3, profile: { role: 'viewer' } }
];

const toRemove = [
  { id: 2, profile: { role: 'editor' } }
];

_.pullAllWith(users, toRemove, _.isEqual);

console.log(users);
// Output:
// [
//   { id: 1, profile: { role: 'admin' } },
//   { id: 3, profile: { role: 'viewer' } }
// ]

Internal Execution and Mutation

When _.pullAllWith executes, it iterates through the elements of the primary array. For each item, it checks against every entry in the removal array using the supplied comparator. If _.isEqual(arrVal, othVal) returns true, the element in array is flagged for removal.

Key operational characteristics include: