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.
_.filterretains truthy values: When the predicate returnstrue(or any truthy value),_.filterincludes the current element in the resulting array. Elements that evaluate tofalse(or falsy) are excluded._.rejectretains falsy values: When the predicate returnsfalse(or any falsy value),_.rejectincludes the current element in the resulting array. Elements that evaluate totrue(or truthy) are excluded.
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.
- Eliminating Negation Overhead: Using
_.rejectremoves the need to write an anonymous wrapper function just to insert the logical NOT operator (!). - Reusing Existing Predicates: If you already have a
validator function like
isExpired(item)orisEven(number), you can pass it directly into_.rejectas a callback (_.reject(items, isExpired)) rather than declaring_.filter(items, item => !isExpired(item)). - 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.