Lodash _.max() method
What you’ll learn
- Why
_.maxtakes one argument, not(array, iteratee)—and how the second-arg myth silently picks the wrong element. - That
_.max([]),_.max(null), and_.max(undefined)all returnundefined(no-InfinitylikeMath.max()). - Which elements are skipped inside the array (
null,undefined,NaN) and the subtle Symbol-position rule (skipped during seeding, throws afterwards). - How
_.maxusesbaseGt(the>operator), so strings sort lexicographically. - Try each example in the editor (
?tryit=1,2,3) with Lodash from a CDN.
Prerequisites
Familiarity with the native Math.max contract (variadic, returns -Infinity on no args) and the Math hub overview. For object-keyed maxima, head straight to _.maxBy() when published.
- Array vs args:
Math.max(a, b, c)spreads;_.max([a, b, c])takes one array. - NaN propagation:
Math.max(NaN, 3)isNaN;_.max([NaN, 3])is3.
Overview
_.max(array) walks the array once, calling baseExtremum(array, identity, baseGt). The loop guards each candidate with current != null && current === current, so nullish and NaN entries are silently skipped. The first valid value seeds the running result; later values replace it only when baseGt(current, computed) (i.e. current > computed) is true.
Single pass
O(n), no allocation beyond the result reference.
Nullish-safe
null, undefined, and NaN never participate. Symbol-first arrays skip; Symbol-mid-array throws.
No iteratee
For mapped comparisons reach for _.maxBy(arr, fn) instead.
Syntax
_.max(array) - array: the array to scan. Falsy or empty input short-circuits.
- Returns: the maximum element (using
>), orundefinedif the array is empty/falsy or every element is skipped.
Lodash docs baseline
The two official examples: a numeric array and the empty case.
import max from "lodash/max";
console.log(max([4, 2, 8, 6])); // 8
console.log(max([])); // undefined
console.log(max(null)); // undefined What gets skipped (NaN, nullish, Symbols)
Where native Math.max(...arr) returns NaN if any element is non-numeric, _.max quietly skips null, undefined, and NaN. Symbols are different: skipped while looking for the first seed, but a Symbol after a non-Symbol value lands in baseGt's > and throws.
import max from "lodash/max";
console.log(max([1, NaN, 3, 2])); // 3
console.log(max([NaN, NaN])); // undefined (every value skipped)
console.log(max([1, null, undefined, 3])); // 3
console.log(max([Symbol("a"), 1, 2])); // 2 (Symbol skipped during seeding)
// Symbol AFTER a seed -> throws
try {
console.log(max([1, Symbol("x"), 2]));
} catch (err) {
console.log("Threw:", err.message); // "Cannot convert a Symbol value to a number"
}
// Compare with native
console.log(Math.max(1, NaN, 3, 2)); // NaN Strings, and the “_.max(arr, iteratee)” trap
Strings compare lexicographically via >. For objects, _.max compares the objects themselves—which is never useful—and silently ignores any second argument. Reach for _.maxBy.
import max from "lodash/max";
import maxBy from "lodash/maxBy";
console.log(max(["banana", "apple", "cherry"])); // "cherry"
const items = [
{ id: 1, value: 15 },
{ id: 2, value: 25 },
{ id: 3, value: 10 }
];
// Trap: _.max ignores the second argument; returns the first item
console.log(max(items, o => o.value)); // { id: 1, value: 15 }
// Correct API
console.log(maxBy(items, o => o.value)); // { id: 2, value: 25 }
console.log(maxBy(items, "value")); // { id: 2, value: 25 } 📋 _.max vs Math.max
| Input | _.max(arr) | Math.max(...arr) |
|---|---|---|
[4, 2, 8, 6] | 8 | 8 |
[] | undefined | -Infinity |
[1, NaN, 3] | 3 | NaN |
[1, null, 3] | 3 | 3 (null coerces to 0) |
['banana', 'cherry'] | 'cherry' | NaN |
arr, iteratee | iteratee ignored | n/a |
The empty-array row is the biggest behavioral difference: _.max([]) is friendlier than -Infinity for “no data” states.
Pitfalls to avoid
There is no iteratee parameter
_.max(items, o => o.value) silently returns the first item because the second argument is ignored. The correct API is _.maxBy(items, fn).
Empty → undefined
Default with ?? when you display it: _.max(scores) ?? 0. || would replace a legitimate 0 with the fallback.
Silent NaN filter
Sometimes you want to know there were bad samples. _.max won’t tell you; pre-validate with arr.some(Number.isNaN) if it matters.
Lexicographic vs numeric
String comparison is by code point: _.max(['9', '10']) is '9'. Coerce first if you have digit strings.
❓ FAQ
Summary
- Purpose: single-pass max over an array, ignoring nullish and
NaNentries (Symbols only skip during seeding; mid-array Symbols throw). - Remember: one argument only; falsy/empty input returns
undefined; strings compare lexicographically. - Next: Lodash _.maxBy() for object arrays, or the official Lodash docs for _.max.
_.max is the one-argument shortcut baseExtremum(array, identity, baseGt). The internal guard silently skips null, undefined, and NaN—_.max([1, NaN, 3]) returns 3, and _.max([NaN, NaN]) returns undefined because every candidate was skipped. Symbols are only skipped while looking for the first seed; a Symbol that appears after a valid value falls through to baseGt's > and throws.
6 people found this page helpful
