Lodash _.cond with Overlapping Truthy Predicates
When using the _.cond method in Lodash, providing pairs
with overlapping truthy predicates results in the execution of only the
first matching predicate and its corresponding transformer function.
Because Lodash evaluates condition-action pairs sequentially from the
beginning of the array to the end, it short-circuits upon encountering
the first truthy check, completely ignoring any subsequent matching
conditions.
How Lodash Evaluates
_.cond
The _.cond method accepts an array of pairs, where each
pair consists of a predicate function and a
transform function:
_.cond([[predicate1, transform1], [predicate2, transform2], ...]);When the generated function is called with arguments, Lodash iterates through the pairs in their defined index order:
- It invokes the
predicatefunction with the supplied arguments. - If the
predicatereturns a truthy value, Lodash immediately invokes the correspondingtransformfunction with the same arguments and returns its result. - Once a match is resolved, iteration terminates immediately without evaluating the remaining predicates.
- If no predicate returns a truthy value, the function returns
undefined.
Example of Overlapping Predicates
Consider a scenario where a number can satisfy multiple conditions, such as being greater than zero and greater than ten:
const _ = require('lodash');
const classifyNumber = _.cond([
[(n) => n > 0, (n) => `${n} is greater than 0`],
[(n) => n > 10, (n) => `${n} is greater than 10`],
[_.stubTrue, (n) => `${n} is 0 or negative`]
]);
console.log(classifyNumber(15));
// Output: "15 is greater than 0"Even though 15 > 10 is true, Lodash encounters
(n) => n > 0 first. Because 15 > 0 is
truthy, the evaluation halts, and the function returns
"15 is greater than 0". The second predicate
(n) => n > 10 is never called.
Practical Implications
Because evaluation order dictates which transform runs, proper ordering of predicate pairs is critical:
- Order by Specificity: Place more specific or restrictive conditions earlier in the array and broader, general conditions toward the end. Swapping the order in the previous example ensures that numbers greater than 10 are caught by the specific condition first:
const classifyNumberCorrected = _.cond([
[(n) => n > 10, (n) => `${n} is greater than 10`],
[(n) => n > 0, (n) => `${n} is greater than 0`],
[_.stubTrue, (n) => `${n} is 0 or negative`]
]);
console.log(classifyNumberCorrected(15));
// Output: "15 is greater than 10"- No Multi-Condition Aggregation:
_.condbehaves like anif ... else if ... elsechain, not independentifstatements. It cannot be used out-of-the-box to run multiple side effects or transformations for all matching predicates simultaneously.