Why Lodash _.reject Is the Opposite of _.filter

In the Lodash JavaScript library, _.filter and _.reject are complementary collection methods designed to create a new array containing a subset of items based on a conditional test. While both methods iterate over a collection and pass each element to a predicate function, they interpret the returned boolean value in inverse ways. This article explains the technical mechanics behind both functions, demonstrates how their boolean evaluation creates an inverse relationship, and highlights why this distinction is valuable for code readability.

The Predicate Function and Boolean Evaluation

To understand why these methods are opposites, you must look at how each handles the output of the predicate function. A predicate is a function that takes an array item and returns a truthy or falsy value.

Because of this design, _.reject(collection, predicate) produces the exact inverse set of elements that _.filter(collection, predicate) produces for the exact same input.

Functional Equivalence

In pure JavaScript, native Array.prototype.filter only supports the retention of truthy results. To reject items natively, developers must explicitly negate the predicate's return value:

// Native JavaScript negation
const activeUsers = users.filter(user => user.isActive);
const inactiveUsers = users.filter(user => !user.isActive);

In Lodash, _.reject acts as a declarative shortcut for this negation logic:

// Lodash filter vs reject
const activeUsers = _.filter(users, 'isActive');
const inactiveUsers = _.reject(users, 'isActive');

Functionally, _.reject(array, fn) is identical to running:

_.filter(array, item => !fn(item));

Why Lodash Provides Both Methods

The primary motivation for including _.reject alongside _.filter is readability and developer ergonomics.

  1. Eliminating Negation Overhead: Using _.reject removes the need to write an anonymous wrapper function just to insert the logical NOT operator (!).
  2. Reusing Existing Predicates: If you already have a validator function like isExpired(item) or isEven(number), you can pass it directly into _.reject as a callback (_.reject(items, isExpired)) rather than declaring _.filter(items, item => !isExpired(item)).
  3. Declarative Shorthands: Lodash supports object-matching and property-name shorthands. Calling _.reject(tasks, { completed: true }) is significantly cleaner than creating an inverted custom filter function.

By providing both functions, Lodash allows developers to express intent directly: use _.filter when you want to specify what to keep, and use _.reject when you want to specify what to discard.