Lodash _.maxBy() method

Beginner
⏱️ 6 min read
📚 Updated: May 2026
🎯 3 Code examples
🚀 3 Try-it labs
Lodash

What you’ll learn

  • How _.maxBy(array, iteratee) projects each element to a score, picks the largest, and returns the original element.
  • The two iteratee shapes Lodash accepts: a function or a _.property-style path string ('n', 'score.value').
  • That elements whose iteratee returns null, undefined, or NaN are silently skipped—handy and dangerous.
  • Why the empty-array result is undefined and how to default it safely with ??.
  • Try each example in the editor (?tryit=1, 2, 3) with Lodash from a CDN.

Prerequisites

Read _.max() first—_.maxBy is the same factory with an iteratee plugged in. The Math hub covers nullish defaults and the *By family idea.

  • Iteratee shorthand: a string is treated as a property path, an object as a partial-match, a function as itself.
  • baseExtremum: the shared loop also powers _.max, _.min, and _.minBy—same skip rules.

Overview

Under the hood: baseExtremum(array, baseIteratee(iteratee, 2), baseGt). Each element is mapped to a score, scores compare via >, and the candidate behind the highest valid score wins. Skipping rules from _.max still apply—null, undefined, and NaN scores are dropped.

Score, then compare

Iteratee runs once per element; > compares the scores; the original element comes back.

Path shorthand

String iteratees become deep property lookups via baseIteratee'a.b.c' just works.

Missing fields skip

If obj.sales is absent, the iteratee returns undefined—that element is ignored entirely.

Syntax

javascript
_.maxBy(array, [iteratee = _.identity])
  • array: array to scan. Falsy or empty input short-circuits.
  • iteratee: function, string path, or object pattern. Default is _.identity—omit it and you get the same result as _.max(array).
  • Returns: the original element with the highest mapped score, or undefined.
1

Lodash docs baseline (function & path)

Both calls match the documented examples: a function iteratee and the _.property shorthand.

javascript
import maxBy from "lodash/maxBy";

const objects = [{ n: 1 }, { n: 2 }];

console.log(maxBy(objects, function (o) { return o.n; })); // { n: 2 }
console.log(maxBy(objects, "n"));                            // { n: 2 }
Try it Yourself
2

Nested paths & missing fields

Dotted paths drill into nested structures. Elements whose iteratee returns undefined—like rows missing the field—are quietly dropped from the contest.

javascript
import maxBy from "lodash/maxBy";

const students = [
  { id: 1, score: { value: 80 } },
  { id: 2, score: { value: 95 } },
  { id: 3, score: { value: 90 } }
];
console.log(maxBy(students, "score.value")); // { id: 2, score: { value: 95 } }

// Missing fields are skipped (note: only the first row has `sales`)
const data = [
  { id: 1, sales: 150 },
  { id: 2, revenue: 200 },
  { id: 3, profit: 180 }
];
console.log(maxBy(data, "sales")); // { id: 1, sales: 150 }
Try it Yourself
3

Empty input, NaN scores, default iteratee

Three edges worth knowing: empty / nullish input returns undefined; NaN scores are filtered out; omitting the iteratee makes _.maxBy behave like _.max.

javascript
import maxBy from "lodash/maxBy";

console.log(maxBy([], "v"));   // undefined
console.log(maxBy(null, "v")); // undefined

// Safe defaulting (use ?? not ||)
const topScore = maxBy([], "v") ?? { v: 0 };
console.log(topScore);          // { v: 0 }

// NaN scores are skipped
console.log(maxBy([{ v: 1 }, { v: NaN }, { v: 3 }], "v")); // { v: 3 }

// No iteratee -> falls back to _.identity
console.log(maxBy([3, 1, 5, 2])); // 5
Try it Yourself

📋 _.maxBy vs _.max vs hand-rolled reduce

ApproachCodeNotes
_.max_.max(arr)Single-arg only; second argument is ignored. Compares values directly.
_.maxBy (function)_.maxBy(arr, o => o.price)Most flexible; iteratee can compute derived values.
_.maxBy (path)_.maxBy(arr, 'price')Shorter; supports dotted paths like 'a.b.c'.
Reducearr.reduce((m, o) => o.price > m.price ? o : m)Verbose, throws on empty arrays, no NaN guard.

The reduce alternative also doesn’t handle the NaN / nullish skipping you get for free here.

Pitfalls to avoid

Defaulting

Don’t use ||

_.maxBy([{n:0}], 'n') || { n: -1 } “works” on empty input but also replaces real {n:0} if you ever expand the guard. Use ??.

Missing fields

Silent skip

If half your rows lack the property you’re ranking by, they vanish from the contest. The result reflects only rows that have the field.

Iteratee shape

No (value, index, array)

The iteratee is invoked with one argument (the value). Don’t reach for an index parameter—use arr.reduce if you need it.

Ties

Stable on ties

When two elements share the maximum score, _.maxBy keeps the first one (later ties fail the strict > check).

❓ FAQ

_.max(array) compares the values directly using >. _.maxBy(array, iteratee) maps each value through the iteratee first and compares the mapped scores—then still returns the original element with the highest score. Use _.maxBy whenever your array holds objects.
Yes. _.maxBy(arr, 'price') is shorthand for _.maxBy(arr, o => o.price). Dotted paths work too: _.maxBy(arr, 'score.value'). This is the same _.property iteratee shorthand the lodash docs document.
_.maxBy([]) and _.maxBy(null) both return undefined—no -Infinity, no throw. Use ?? (not ||) when defaulting so a legitimate falsy max isn't replaced.
Those elements are silently skipped by baseExtremum's current === current guard. Only elements with finite, comparable scores compete. If every element is skipped, you get undefined.
Use import maxBy from "lodash/maxBy"; or const maxBy = require('lodash/maxBy'). Tree-shake-friendly and only pulls in baseExtremum, baseGt, and baseIteratee.

Summary

  • Purpose: find the “biggest” element of an array using a derived score; returns the original element.
  • Remember: function or string-path iteratee; missing or NaN scores are skipped; empty/null input → undefined.
  • Next: Lodash _.mean(), or the official Lodash docs for _.maxBy. Pair with _.minBy for symmetric ranking.
Did you know?

_.maxBy calls the iteratee with two arity hint (baseIteratee(iteratee, 2)), which is what lets the property-path shorthand like 'score.value' work the same way as obj => _.get(obj, 'score.value')—no extra parsing on your side.

About the author

Mari Selvan M P
Mari Selvan M P 🔗

Developer, cloud engineer, and technical writer

  • Experience 12 years building web and cloud systems
  • Focus Full Stack Development, AWS, and Developer Education

I write practical tutorials so students and working developers can learn by doing—from databases and APIs to deployment on AWS.

6 people found this page helpful