SnippetJavaScript

Group an array by a key

Buckets an array into an object keyed by a function you provide, built on Array.reduce for engines that predate Object.groupBy.

Last updated

Snippetjavascript
function groupBy(items, keyFn) {
  return items.reduce((groups, item) => {
    const key = keyFn(item);
    (groups[key] ??= []).push(item);
    return groups;
  }, {});
}

// groupBy(users, (u) => u.role) -> { admin: [...], user: [...] }

How it works

This ships as a reduce-based groupBy rather than the newer Object.groupBy on purpose: Object.groupBy landed in ES2024 (Node 21+, current evergreen browsers only as of 2024), while reduce runs everywhere, including older Safari and any bundler target below ES2022. If your project's baseline is recent enough, Object.groupBy(items, keyFn) does the same job in one line and returns a null-prototype object instead of a plain {}.

groups[key] ??= [] reads as "create the bucket the first time this key is seen, otherwise reuse it" — the logical-nullish-assignment operator replaces the older `groups[key] = groups[key] || []` pattern, which would wrongly reset a falsy-but-valid bucket (it can't happen here since the value is always an array, but the shorter form is also just clearer intent).

Edge cases to know

  • Keys are coerced to strings (or symbols) because they become object property names — grouping by a Date or an object reference won't give you back the original key type.
  • Object.groupBy is a drop-in replacement once your target supports it, but note it groups into a null-prototype object, so hasOwnProperty and similar Object.prototype methods aren't available on the result the way they are here.
  • For very large arrays where insertion order across many keys matters, a Map keyed by keyFn(item) avoids the string-coercion issue above.

Related in Snippets