Lodash _.partition Collection Mutation Behavior

This article explores how Lodash's _.partition method behaves when a predicate function conditionally mutates the underlying collection during evaluation. It breaks down the internal iteration mechanics of Lodash, the consequences of index shifting and cached bounds, and why in-place mutations lead to non-deterministic partitioning results.


Internal Iteration Mechanics of _.partition

In Lodash, _.partition is built on top of internal aggregation utilities (such as createAggregator). When invoked on an array, it iterates sequentially from index 0 up to length - 1 using an internal loop structure similar to arrayEach.

Crucially, the iteration bounds—specifically the array's length—are generally evaluated at the start of the iteration cycle, and an internal counter increments monotonically by 1 on each step. Each element at the current index is passed to the predicate function, and depending on whether the return value is truthy or falsy, the element is pushed into one of two result arrays: [pass, fail].

What Happens When Predicates Mutate the Collection

Because JavaScript arrays are reference types and Lodash iterates directly over the source reference without cloning it beforehand, modifying the collection inside the predicate directly alters the execution context of subsequent iterations.

1. Deleting Elements (e.g., splice)

If the predicate removes an element using methods like Array.prototype.splice():

const numbers = [1, 2, 3, 4, 5];

const [evens, odds] = _.partition(numbers, (num, index, arr) => {
  if (num === 2) {
    arr.splice(index, 1); // Mutating the source array
    return true;
  }
  return num % 2 === 0;
});

// Result:
// - `3` shifted into index 1 and was skipped.
// - `evens` receives [2, 4]
// - `odds` receives [1, 5]
// - `numbers` is permanently modified to [1, 3, 4, 5]

2. Adding Elements (e.g., push, unshift)

3. In-Place Element Property Mutation

If the predicate mutates the properties of an individual element rather than changing the array's structure, the mutation persists. The partitioned arrays store shallow references to the original objects. Any mutation applied to an object during the predicate's execution will be reflected in both the source array and the output partitions.

Summary of Behavioral Risks

To ensure predictable results, predicates passed to _.partition should remain pure functions without side effects. If the source collection must be modified, clone the collection prior to partitioning or perform mutations in a separate processing step.