Lodash _.meanBy() method
What you’ll learn
- How
_.meanBy(array, iteratee)is justbaseSum(array, baseIteratee(iteratee, 2)) / array.length. - The two iteratee shapes: a function or a
_.propertypath string ('n','duration.minutes'). - The missing-field trap: rows without the property are silently dropped from the sum but still counted in the divisor.
- Why
_.meanBy([], 'n')isNaN(notundefined) and how to guard withNumber.isFinite(). - The same string-concatenation trap inherited from
_.mean: one stringy iteratee result wrecks the answer.
Prerequisites
Read _.mean() first—_.meanBy is the same engine with an iteratee layer in front. The skip-rules and string-concat traps documented there all apply.
- Iteratee shorthand: string → property path, function → itself.
- baseSum / baseMean: they don’t care if results are numbers—use
+blindly and divide.
Overview
The whole function is two lines: baseMean(array, baseIteratee(iteratee, 2)). baseIteratee turns a string or function into a per-element score function; baseMean sums those scores and divides by array.length. Same skip-rules and string traps as _.mean, plus one extra footgun: a row whose iteratee returns undefined is silently excluded from the sum but still increments the divisor.
Path shorthand
Dotted paths work: 'a.b.c'. Same machinery as _.maxBy, _.minBy, _.sumBy.
Missing-field trap
Skipped values lower the sum but not the divisor → partial data pulls the mean toward zero.
Empty → NaN
_.meanBy([], 'n') and _.meanBy(null, 'n') both return NaN—guard with Number.isFinite.
Syntax
_.meanBy(array, [iteratee = _.identity]) - array: array to process. Falsy or empty →
NaN. - iteratee: function or string property path. Default is
_.identity—equivalent to_.mean(array). Pass an iteratee whenever you have objects. - Returns:
number—the arithmetic mean of the projected values.
Docs baseline — function vs path shorthand
The official examples: both calls return 5, demonstrating that the function form and the _.property shorthand are interchangeable.
import meanBy from "lodash/meanBy";
const objects = [{ n: 4 }, { n: 2 }, { n: 8 }, { n: 6 }];
console.log(meanBy(objects, function (o) { return o.n; })); // 5
console.log(meanBy(objects, "n")); // 5
// Nested path
const records = [
{ duration: { minutes: 30 } },
{ duration: { minutes: 45 } },
{ duration: { minutes: 60 } }
];
console.log(meanBy(records, "duration.minutes")); // 45 The missing-field trap
Half your rows don’t have the field you’re averaging? Lodash silently drops their values from the sum, but the divisor is still array.length. The result looks like a real average, just… wrong. Filter the array down to rows that have the field if you want a correct answer.
import meanBy from "lodash/meanBy";
const rows = [
{ n: 1 },
{ x: 2 }, // no 'n' property -> iteratee returns undefined
{ n: 3 }
];
console.log(meanBy(rows, "n")); // 1.3333... (4 / 3, NOT 2)
// Correct: filter to rows that have the field, then average
const usable = rows.filter(r => r.n != null);
console.log(meanBy(usable, "n")); // 2
// null values DO count (coerced to 0); only undefined is skipped
const withNull = [{ n: 1 }, { n: null }, { n: 3 }];
console.log(meanBy(withNull, "n")); // 1.3333... — (1 + 0 + 3) / 3
// String values in the iteratee result still trigger concatenation
const mixed = [{ n: 1 }, { n: "20" }, { n: 3 }];
console.log(meanBy(mixed, "n")); // 401 — '120' + 3 = '1203', / 3 = 401 Empty input, no-iteratee fallback, complex iteratees
Empty / nullish input yields NaN. Omitting the iteratee makes _.meanBy behave like _.mean — great for raw numeric arrays, useless for object arrays. Custom function iteratees let you derive scores on the fly.
import meanBy from "lodash/meanBy";
console.log(meanBy([], "n")); // NaN
console.log(meanBy(null, "n")); // NaN
// Safe default with Number.isFinite (?? would not catch NaN)
function safeMeanBy(arr, it) {
const m = meanBy(arr, it);
return Number.isFinite(m) ? m : 0;
}
console.log(safeMeanBy([], "n")); // 0
// No iteratee -> _.identity. Works for numbers, NOT for objects.
console.log(meanBy([10, 20, 30])); // 20 (same as _.mean)
console.log(meanBy([{ n: 1 }, { n: 2 }])); // NaN — can't average objects
// Complex iteratee: derive a score per row
const orders = [
{ qty: 2, price: 10 },
{ qty: 1, price: 25 },
{ qty: 3, price: 5 }
];
console.log(meanBy(orders, o => o.qty * o.price)); // 20 (20 + 25 + 15 = 60 / 3) 📋 _.meanBy vs _.mean vs reduce
| Input | Code | Result | Notes |
|---|---|---|---|
[10, 20, 30] | _.mean(arr) | 20 | Use _.mean for raw number arrays. |
[{n:1},{n:2},{n:3}] | _.meanBy(arr, 'n') | 2 | Path shorthand — the common case. |
[{a:{b:5}},{a:{b:7}}] | _.meanBy(arr, 'a.b') | 6 | Dotted path drills in. |
[{n:1},{x:2},{n:3}] | _.meanBy(arr, 'n') | 1.33 | Missing field → partial data lowers the mean. |
[{n:1},{n:2}] | arr.reduce((a,o)=>a+o.n,0)/arr.length | 1.5 | Same result; you write the validation yourself. |
Pitfalls to avoid
Silent skip ≠ correct mean
undefined is dropped from the sum but the divisor still counts the row. Filter the array first if “average over rows that have the field” is what you mean.
NaN not undefined
NaN ?? 0 is still NaN. Use Number.isFinite() to default.
Concatenation trap inherited from _.mean
If the iteratee returns a string for any row, the running sum becomes a string and the final divide produces nonsense. Cast in the iteratee: o => Number(o.n).
Default iteratee = _.identity
Forgetting the iteratee on an array of objects gives NaN — objects don’t sum. Always pass a path or function for object arrays.
Throws
If your iteratee returns BigInt values, you’ll get Cannot mix BigInt and other types. Convert with Number() in the iteratee.
❓ FAQ
Summary
- Purpose: average an array of values via a per-element function or property-path iteratee.
- Remember: empty/null →
NaN; missing fields lower the sum but not the divisor; stringy results concatenate; default iteratee fails on objects. - Next: Lodash _.min() or the official Lodash docs for _.meanBy.
_.meanBy(rows, 'n') divides by rows.length—not by the count of rows that actually had an n. A missing field drops the value from the sum, not from the divisor, so partial data quietly drags the mean toward zero.
6 people found this page helpful
