Lodash _.subtract() method
What you’ll learn
- How
_.subtractis built withcreateMathOperation((a, b) => a - b, 0)—sharing the factory with_.add,_.multiply, and_.divide. - The non-commutative
undefinedtrap:_.subtract(undefined, 5)returns5, not-5. - That
nullcoerces to0and produces the correct sign—making it the safer placeholder. - Why stringy numeric inputs coerce correctly (no concatenation trap like
_.add). - How to use
_.subtractinsidereducefor running differences, and why BigInt throws.
Prerequisites
Skim _.add() first—same factory, identity 0. The factory’s undefined rules behave well for _.add (commutative) but bite for _.subtract.
- Commutativity:
a + b == b + a, buta - b != b - a. Matters for theundefinedrule. - Native
-coercion: always tries to make a number; never concatenates strings.
Overview
The implementation is two lines: createMathOperation((a, b) => a - b, 0). The factory’s rules: both undefined → 0 (identity); one undefined → return the other operand (no math runs); otherwise coerce via baseToNumber (or baseToString if either is a string) and apply -. The “return the other operand” rule is mathematically correct for + and * but produces the wrong sign for -.
Identity is 0
Same as _.add. Useful for reduce-on-empty.
Non-commutative trap
_.subtract(undefined, 5) returns 5, not -5. The factory wasn’t designed for this case.
No precision fix
0.3 - 0.2 still produces 0.09999…. Lodash uses native -.
Syntax
_.subtract(minuend, subtrahend) - minuend: the number to subtract from.
- subtrahend: the number to subtract.
- Returns:
number—the difference.0if both inputs areundefined; the lone operand untouched if one isundefined.
Basics — numbers, strings, precision
The docs example plus the common stringy-input case (which works numerically, unlike _.add) and the floating-point reality.
import subtract from "lodash/subtract";
console.log(subtract(6, 4)); // 2 (docs example)
console.log(subtract(10, 5)); // 5
console.log(subtract(15, 7)); // 8
// Stringy numeric inputs coerce numerically (no '+' trap)
console.log(subtract("10", "3")); // 7
console.log(subtract(10, "3")); // 7
console.log(subtract("abc", 3)); // NaN
// Map a constant adjustment over an array
console.log([10, 20, 30, 40].map(v => subtract(v, 5))); // [5, 15, 25, 35]
// Floating-point: Lodash does NOT fix precision
console.log(subtract(0.3, 0.2)); // 0.09999999999999998
console.log(subtract(0.1 + 0.2, 0.3)); // 5.551115123125783e-17 The undefined sign-flip trap
The most surprising thing about _.subtract: undefined on the left side does not become 0 — the factory just returns the right operand. So 0 − 5 never happens. Use null instead when you need a missing minuend to behave like zero.
import subtract from "lodash/subtract";
// Both undefined: identity
console.log(subtract(undefined, undefined)); // 0
// One undefined: returns the OTHER operand untouched (no math!)
console.log(subtract(undefined, 5)); // 5 BUG-PRONE: expected -5
console.log(subtract(5, undefined)); // 5 OK: 5 - 0 would also be 5
// null coerces to 0 -> correct math
console.log(subtract(null, 5)); // -5 correct
console.log(subtract(5, null)); // 5
console.log(subtract(null, null)); // 0
// Defensive helper
function safeSubtract(a, b) {
return subtract(a ?? 0, b ?? 0); // treat both undefined & null as 0
}
console.log(safeSubtract(undefined, 5)); // -5
console.log(safeSubtract(5, undefined)); // 5 Reduce, edge cases, and BigInt
Use _.subtract as a reduce callback for running differences. Identity is 0, so empty-array reduce-with-initial-value works cleanly. BigInt throws.
import subtract from "lodash/subtract";
// Running differences
console.log([100, 10, 20, 30].reduce(subtract)); // 40 (100 - 10 - 20 - 30)
console.log([10, 20, 30].reduce(subtract, 100)); // 40
console.log([].reduce(subtract, 100)); // 100 (initial value untouched)
// Infinity & NaN propagation
console.log(subtract(Infinity, 1)); // Infinity
console.log(subtract(Infinity, Infinity)); // NaN
console.log(subtract(NaN, 5)); // NaN
// Sign preservation: -0 vs 0
console.log(subtract(0, 0)); // 0
console.log(subtract(-0, 0)); // -0
// BigInt: throws
try {
console.log(subtract(5n, 2n));
} catch (err) {
console.log("Threw:", err.message); // "Cannot convert a BigInt value to a number"
}
// Native works for BigInt
console.log(5n - 2n); // 3n 📋 _.subtract vs native -
| Input | _.subtract(a, b) | a - b | Notes |
|---|---|---|---|
6, 4 | 2 | 2 | Same. |
undefined, undefined | 0 | NaN | Lodash returns identity. |
undefined, 5 | 5 | NaN | Lodash returns the lone operand—wrong sign for subtraction. |
5, undefined | 5 | NaN | Same factory rule; mathematically “5 - 0” would also be 5, so it’s consistent here. |
null, 5 | -5 | -5 | Both treat null as 0. |
"10", "3" | 7 | 7 | Both coerce numerically (- never concatenates). |
0.3, 0.2 | 0.099…8 | 0.099…8 | Same float-precision result. |
5n, 2n | throws | 3n | Native - supports BigInt; Lodash does not. |
Pitfalls to avoid
Sign-flip trap
_.subtract(undefined, 5) is 5, not -5. If the minuend might be undefined, coalesce to 0 first (a ?? 0) or use null instead.
Not a precision fix
_.subtract(0.3, 0.2) is 0.09999999999999998. The old reference’s “addresses precision challenges” framing is wrong—Lodash just wraps -.
Behave differently
null coerces to 0 (correct math). undefined short-circuits (returns the other operand). Don’t treat them as interchangeable.
Throws
Use the native - operator for BigInt math.
Silent NaN
Numeric strings work ("10" - 3 = 7) but "abc" gives NaN with no error. Validate when input is untyped.
❓ FAQ
Summary
- Purpose: pairwise subtraction built on
createMathOperation(-, 0). - Remember:
_.subtract(undefined, 5)is5(sign-flip trap);nullcoerces to0; stringy numerics coerce numerically; non-numeric strings →NaN; BigInt throws; precision unfixed. - Next: Lodash _.sum(), or read the official Lodash docs for _.subtract.
_.subtract(undefined, 5) returns 5, not -5. The createMathOperation factory short-circuits whenever one operand is undefined and returns the other operand untouched—sound for commutative _.add and _.multiply, mathematically wrong for non-commutative _.subtract. Use null as a placeholder (it coerces to 0 properly) or guard before calling.
6 people found this page helpful
