Lodash _.min() method
What you’ll learn
- How
_.min(array)walks the array exactly once viabaseExtremum(array, identity, baseLt). - Why empty / null / undefined input returns
undefined(vsMath.min()'sInfinity). - That
null,undefined, andNaNelements are silently skipped; Symbol mid-array throws. - That
_.mintakes only one argument—any iteratee you pass is ignored. Use _.minBy() for objects. - How strings compare lexicographically and why mixed-type arrays are order-dependent.
Prerequisites
Read _.max() first—same engine (baseExtremum), opposite comparator. Skim the Math hub for the “nullish identity” defaults across the family.
- JavaScript
<operator: string vs string is lexicographic; number vs string coerces toNumberand givesNaNwhen invalid. - Nullish defaulting: prefer
??over||when0or empty string are legitimate values.
Overview
The implementation is six lines: (array && array.length) ? baseExtremum(array, identity, baseLt) : undefined. Inside baseExtremum, the running “computed” value is seeded by the first element that passes value != null && value === value && !isSymbol(value). Every subsequent element is compared with baseLt (just value < computed) and replaces the result on a strict less-than.
Single pass, strict <
No sort, no copy. The first occurrence wins on ties because the new value must be strictly smaller.
Friendlier empty than Math.min
_.min([]) → undefined. Math.min() → Infinity. The first is easier to default safely.
No iteratee—use _.minBy
A second argument is silently ignored. For object arrays reach for _.minBy(arr, 'n').
Syntax
_.min(array) - array: the array to scan. Falsy / empty input short-circuits.
- Returns: the minimum value, or
undefinedif every element was skipped or the input was empty. - No iteratee.
_.min(arr, fn)still works but thefnargument is dropped. Use _.minBy() when you need that.
Basics — numbers, strings, empty
The bread-and-butter: integer arrays, lexicographic string min, and the empty/falsy → undefined contract. Note the strict <: ties keep the first occurrence.
import min from "lodash/min";
console.log(min([4, 2, 8, 6])); // 2 (docs example)
console.log(min([4, 7, 1, 9, 12, 5])); // 1
console.log(min([-3, -7, -1])); // -7 (all negatives)
console.log(min([3, 0, 5])); // 0 (zero is valid)
// Strings compare lexicographically
console.log(min(["banana", "apple", "cherry"])); // "apple"
console.log(min(["10", "9", "2"])); // "10" ('1' < '2' < '9')
// Empty / falsy input
console.log(min([])); // undefined
console.log(min(null)); // undefined
console.log(min(undefined)); // undefined What gets skipped — NaN, nullish, Symbols
The seed-guard inside baseExtremum rejects null, undefined, NaN, and Symbols. The catch: Symbols are only skipped while looking for the first valid seed. A Symbol that shows up after a non-Symbol value falls through to baseLt’s < operator and throws.
import min from "lodash/min";
console.log(min([4, NaN, 2])); // 2 (NaN skipped)
console.log(min([NaN, NaN])); // undefined (every candidate skipped)
console.log(min([4, null, 2])); // 2 (null skipped)
console.log(min([4, undefined, 2])); // 2 (undefined skipped)
// Symbol at the start: skipped during seeding
console.log(min([Symbol("x"), 1, 2])); // 1
// Symbol after a non-Symbol: throws
try {
console.log(min([1, Symbol("x"), 2]));
} catch (err) {
console.log("Threw:", err.message);
// -> "Cannot convert a Symbol value to a number"
} Defaulting safely — ?? not ||
A classic bug: _.min(arr) || default looks fine until the actual minimum is 0 (or ''), and your default replaces a real value. Use ?? or an explicit === undefined check.
import min from "lodash/min";
const scores = [3, 0, 5];
console.log(min(scores) || -1); // -1 BUG: real min is 0
console.log(min(scores) ?? -1); // 0 OK
// Defaulting empty arrays
console.log(min([]) ?? 0); // 0 (no real min, default kicks in)
// BigInt arrays compare correctly
console.log(min([3n, 1n, 5n])); // 1n
// Mixed types: order-dependent, NaN-from-coercion kills updates
console.log(min([5, "a", 3])); // 3 ("a" doesn't beat 5; 3 does)
console.log(min(["a", 5, 3])); // "a" ('a' seeded first; 5 < 'a' is NaN) 📋 _.min vs Math.min
| Input | _.min(arr) | Math.min(...arr) | Notes |
|---|---|---|---|
[4, 2, 8, 6] | 2 | 2 | Same answer. |
[] | undefined | Infinity | Lodash is more defensive; Math.min's identity for ×-spread is Infinity. |
[4, NaN, 2] | 2 | NaN | Lodash skips NaN; Math.min poisons. |
[3n, 1n, 5n] | 1n | throws | Lodash works on pure BigInts; Math.min can't convert BigInt. |
[1, Symbol('x'), 2] | throws | throws | Same root cause: Symbol < n fails. |
["b", "a"] | "a" | NaN | Lodash supports lexicographic; Math.min coerces to Number → NaN. |
Pitfalls to avoid
|| swallows zero
_.min(arr) || 100 turns a legitimate min of 0 into 100. Use ?? or compare to undefined explicitly.
Second argument is dropped
_.min([{n:3},{n:1}], 'n') returns {n:3} (the seed), not {n:1}. Use _.minBy when you need an iteratee.
Order-dependent answers
_.min([5, "a", 3]) is 3 but _.min(["a", 5, 3]) is "a". Sanitize input to a single type first.
Position matters
Symbol-first arrays work; Symbol-after-a-seed throws. If user data might contain symbols, filter them out: arr.filter(v => typeof v !== 'symbol').
Lex, not numeric
_.min(["10", "9", "2"]) returns "10", not "2". Cast first: arr.map(Number).
❓ FAQ
Summary
- Purpose: single-pass minimum of an array using
baseLt(<); skips nullish andNaNvalues. - Remember: empty/null →
undefined; no iteratee (use_.minBy); Symbol mid-array throws; strings compare lexicographically. - Next: Lodash _.minBy() for the iteratee form, or read the official Lodash docs for _.min.
Don’t reach for || when defaulting _.min(arr)—_.min([3, 0, 5]) || -1 returns -1 instead of the real minimum 0. Use ?? (or check === undefined) and your defaults will only fire on truly empty input.
6 people found this page helpful
