How to group arrays in JavaScript without reduce()

2 min read
0 views

Front-end developers frequently transform arrays: filtering, mapping, reducing. And sometimes grouping. Grouping used to require custom reduce() logic, which often felt like more boilerplate than it was worth.

But that’s changing. JavaScript now has native support for grouping data with Object.groupBy() and Map.groupBy(). These static methods make grouping expressive, concise, and far more readable, without the need for external libraries or complex reduce() patterns.

What are Object.groupBy() and Map.groupBy()?

Both of these methods were introduced in ES2024 and allow you to group elements of an array by a key generated from a callback function.

Object.groupBy(array, callback)

This method returns a plain JavaScript object. The keys are strings derived from your callback, and the values are arrays of elements that match each key.

Map.groupBy(array, callback)

This method returns a Map, which allows for non-string keys and maintains insertion order.

Quick examples

Object.groupBy()

const items = ['apple', 'banana', 'orange', 'blueberry'];

const grouped = Object.groupBy(items, item => item[0]);

console.log(grouped);
// {
//   a: ['apple'],
//   b: ['banana', 'blueberry'],
//   o: ['orange']
// }

The callback returns the first letter of each fruit, grouping them accordingly.

Map.groupBy()

const items = [1.1, 2.3, 2.4, 3.5];

const grouped = Map.groupBy(items, Math.floor);

console.log(grouped);
// Map {
//   1 => [1.1],
//   2 => [2.3, 2.4],
//   3 => [3.5]
// }

Because Map.groupBy() can use non-string keys (like numbers or objects), it’s more flexible in certain scenarios.

Replacing reduce() with groupBy()

Before groupBy(), grouping required using reduce() manually:

const grouped = items.reduce((acc, item) => {
  const key = item[0];
  if (!acc[key]) {
    acc[key] = [];
  }
  acc[key].push(item);
  return acc;
}, {});

Now, with Object.groupBy():

const grouped = Object.groupBy(items, item => item[0]);

The groupBy() method reduces noise in your code. You write what you want to do, not how to do it. The result is more expressive and easier to reason about.

When should you use Object.groupBy() vs. Map.groupBy()?

Use Object.groupBy():

  • You only need string keys
  • You want a JSON-serializable result
  • You’re working with plain objects

Use Map.groupBy():

  • You need non-string keys (numbers, objects)
  • You care about insertion order of keys
  • You need to use .keys(), .values(), .entries()

Everyday use: organizing by status

Let’s say you have a list of tasks, and you want to group them by status:

const tasks = [
  { id: 1, title: 'Design', status: 'todo' },
  { id: 2, title: 'Develop', status: 'in-progress' },
  { id: 3, title: 'Test', status: 'todo' },
  { id: 4, title: 'Deploy', status: 'done' },
];

const grouped = Object.groupBy(tasks, task => task.status);

console.log(grouped);
// {
//   todo: [{...}, {...}],
//   in-progress: [{...}],
//   done: [{...}]
// }

Simple and readable.

More practical use cases

Group by computed range

const products = [
  { name: 'Basic', price: 10 },
  { name: 'Pro', price: 50 },
  { name: 'Enterprise', price: 200 },
];

const grouped = Object.groupBy(products, product => {
  if (product.price < 20) return 'budget';
  if (product.price < 100) return 'mid-range';
  return 'premium';
});

console.log(grouped);
// {
//   budget: [...],
//   'mid-range': [...],
//   premium: [...]
// }

Grouping logs by severity

const logs = [
  { message: 'Login', severity: 'info' },
  { message: 'Crash', severity: 'error' },
  { message: 'Timeout', severity: 'warning' },
  { message: 'Load', severity: 'info' },
];

const grouped = Object.groupBy(logs, log => log.severity);

This pattern is super common in dashboards, dev tools, and analytics.

Gotchas to watch out for

Object.groupBy() always stringifies keys

That means:

const result = Object.groupBy([1, '1'], x => x);
console.log(result); // { '1': [1, '1'] }

Both 1 and '1' end up under the string key "1".

If you want distinct non-string keys, use Map.groupBy() instead.

Map.groupBy() isn’t JSON-serializable

If you try:

JSON.stringify(Map.groupBy([1, 2, 3], x => x % 2));
// TypeError: Converting circular structure to JSON

Maps are great for runtime logic, but not suitable for API responses or localStorage. Use Object.groupBy() when working with JSON.

Browser support

groupBy() is supported in all modern browsers (Chrome 117+, Firefox 119+, Safari 17.4+, Edge 117+) and in Node.js 21+.

Here’s a basic Object.groupBy() polyfill for older environments:

function groupByPolyfill(array, callback) {
  return array.reduce((acc, item) => {
    const key = callback(item);
    acc[key] ??= [];
    acc[key].push(item);
    return acc;
  }, {});
}

This polyfill won’t cover every edge case, but works well for most typical use cases.

The next move is yours

Object.groupBy() and Map.groupBy() are elegant additions to JavaScript’s standard library. They replace verbose reduce() patterns and make your data transformations more expressive and declarative.

Here’s a quick comparison of when to use each:

Use case Object.groupBy() Map.groupBy()
String keys only
Non-string keys (numbers, objects)
JSON serialization
Preserving insertion order
Iteration with .entries()
Simplicity and readability

If you’re still reaching for reduce(), give these a try. You might not look back.