Map.groupBy()

The same grouping as Object.groupBy, into a Map instead. That one difference matters whenever the grouping key is not naturally a string.

Map static methodES2024Live demo
Common call
Map.groupBy(items, x => x.date)
Returns
a Map of key → array
Replaces
a reduce that pushes into a Map
Watch out
object keys group by REFERENCE, not by contents
Map.groupBy(itemsitems — Any iterable — an array, a Set, a generator.type: iterable · required, callbackcallback — Called as callback(element, index). Its return value becomes the group key, used as-is with no coercion — matched by SameValueZero.type: Function · required)
→ Map

Demo

Live evaluation
Try:
Inputs
itemsnumber[]numbers, comma separated
Output
[...Map.groupBy([1, 2, 3, 4], n => n % 2 ? 'odd' : 'even')]
[['odd', [1, 3]], ['even', [2, 4]]]

The result is spread to pairs so the grouping is visible. Order within each bucket follows the input, and a group appears only if something landed in it — the second case has no 'even' entry at all, so map.get('even') is undefined rather than an empty array. The real advantage over Object.groupBy is invisible with string keys like these: return a Date or an object from the callback and it stays a Date or an object here, where Object.groupBy would stringify it.

Parameters

NameTypeRequiredDescription
itemsiterableyesAny iterable — an array, a Set, a generator.
callbackFunctionyesCalled as callback(element, index). Its return value becomes the group key, used as-is with no coercion — matched by SameValueZero.

Return value

Map — A Map from each key the callback returned to an array of the items that produced it. Keys keep their original type — nothing is stringified.

Common patterns

Group by a non-string key
The reason this exists alongside Object.groupBy.
const byDate = Map.groupBy(events, e => e.date.getTime());
Group by an object reference
Impossible with a plain object.
const byOwner = Map.groupBy(tasks, t => t.owner);
Default for an empty bucket
Missing keys are undefined.
const odds = groups.get("odd") ?? [];

Examples

1. Grouped
[...Map.groupBy([1, 2, 3], n => n % 2 ? "odd" : "even")]
Returns
[['odd', [1, 3]], ['even', [2]]]
2. It is a Map
Map.groupBy([1], () => "a") instanceof Map
Returns
true
3. Keys keep their type
const k = {id: 1}; Map.groupBy([1], () => k).get(k)
Returns
[1]
4. Object.groupBy stringifies
Object.keys(Object.groupBy([1], () => ({id: 1})))
Returns
['[object Object]']
5. Missing bucket
Map.groupBy([1], () => "odd").get("even")
Returns
undefined
6. Empty input
Map.groupBy([], x => x).size
Returns
0

Pitfalls

1. Object keys group by reference
Returning a fresh object from the callback creates a NEW group for every item, because no two are the same key. Grouping by a derived object rather than by an existing reference gives one bucket per element.
One group each
Map.groupBy([1, 2], x => ({v: x % 2})).size
2
A primitive key
Map.groupBy([1, 2], x => x % 2).size
2 // but by value, correctly
2. Absent groups are undefined, not empty arrays
Same as Object.groupBy. Iterating a known list of categories and reading each from the Map gives undefined for the empty ones, which then throws on .length or .map.
Throws
Map.groupBy([1], () => "odd").get("even").length
TypeError: Cannot read properties of undefined
Default it
(Map.groupBy([1], () => "odd").get("even") ?? []).length
0
3. Dates need a primitive key
Two Date objects for the same instant are different references, so grouping by the Date itself splits them. Group by getTime or an ISO string, then convert back if you need a Date.
Separate groups
Map.groupBy([a, b], x => new Date(x.day)).size
one group per item
Group by the value
Map.groupBy([a, b], x => new Date(x.day).getTime()).size
grouped correctly
4. ES2024 — check your runtime
Node 21+ and 2024-era browsers. The reduce it replaces works everywhere and is only three lines, which is still the right fallback for older targets.
Missing
Map.groupBy(xs, f)
TypeError: Map.groupBy is not a function
Reduce
xs.reduce((m, x) => {
  const k = f(x);
  m.set(k, [...(m.get(k) ?? []), x]);
  return m;
}, new Map())
same shape

When to use

Use it
  • Grouping by a key that is not a string — a number, a Date value, an object reference
  • Preserving the insertion order of first appearance for numeric keys
  • Grouping where a key might collide with an object property name
Reach for something else
  • String keys you will serialise → Object.groupBy
  • You want counts rather than items → map over the groups afterwards
  • Keys are freshly created objects → every item gets its own group
  • Targeting runtimes older than 2024 → a reduce

Notes

Complexity
O(n) — one pass, one callback per item
Return
A new Map whose values are new arrays
CPython impl
V8: Builtins-map-groupby
Memory
Allocates the Map plus one array per group
Thread-safe
Single-threaded; the iterable is consumed once

FAQ

Map.groupBy when the key is not a string — it stays a number, a Date, an object. Object.groupBy when string keys are natural and you want a plain object you can serialise. Object.groupBy also gives a null-prototype result, which has its own quirks.

Map.groupBy(xs, x => x.id);        // number keys kept
Object.groupBy(xs, x => x.status);  // string keys, plain object

History

ES2024
Map.groupBy and Object.groupBy added together, after an earlier Array.prototype.group proposal was withdrawn.