Explain Codes LogoExplain Codes Logo

Most Efficient Method to Groupby on an Array of Objects

javascript
prompt-engineering
functions
performance
Nikita BarsukovbyNikita Barsukov·Dec 15, 2024
TLDR

Grouping objects efficiently by a property using Array.prototype.reduce() is your go-to method:

const groupBy = (arr, prop) => arr.reduce((acc, item) => { // group them like there's no tomorrow (acc[item[prop]] = acc[item[prop]] || []).push(item); return acc; }, {}); // Yeehaw! Let's see it in action. const result = groupBy([{id: 1, val: 'A'}, {id: 1, val: 'B'}, {id: 2, val: 'C'}], 'id');

This fun little function sorts items by id, creating a slickly efficient lookup object where keys match id values, and values are arrays of the associated objects.

Supercharge with a 'for' loop

Ever felt the need for speed with large datasets? A classic for loop can get you there:

const groupByForLoop = (arr, prop) => { const grouped = {}; // fastest loop in the Wild West for (let i = 0; i < arr.length; i++) { let key = arr[i][prop]; // Surprise! You got your own group, cowboy! if (!grouped[key]) grouped[key] = []; grouped[key].push(arr[i]); } return grouped; };

This good ol' for loop dodges those pesky function call overheads, shooting straight for the performance bulls-eye.

Added flexibility with ES6 Map

If maintaining order and flexibility in your groups ticks your box, an ES6 Map would be your partner in crime:

const groupByMap = (arr, prop) => { return arr.reduce((acc, item) => { const key = item[prop]; const collection = acc.get(key) || []; acc.set(key, [...collection, item]); return acc; }, new Map()); // Order, order! Even in the Wild West };

Here, the insertion order remains intact, a key aspect for certain, more finicky, applications.

Group and total

Have the need to group and sum values? Why waste a second trip when you can do it all at once?

const groupBySum = (arr, groupProp, sumProp) => { return arr.reduce((acc, item) => { let key = item[groupProp]; let val = item[sumProp]; // grouping and summing, two birds with one stone! acc[key] = (acc[key] || 0) + val; return acc; }, {}); };

This method integrates the summation right into your groupBy function, streamlining efficiency by reducing redundant array traversals.

TypeScript: Embracing type safety

For the TypeScript tinkerers, adding type safety to groupBy functions shields the data from type-related calamities:

function groupBy<T, K extends keyof T>(arr: T[], prop: K): Record<T[K], T[]> { return arr.reduce((acc: Record<T[K], T[]>, item: T) => { const groupKey = item[prop]; // Immutable law of TypeScript: Use types or else... if (!acc[groupKey]) acc[groupKey] = []; acc[groupKey].push(item); return acc; }, {} as Record<T[K], T[]>); } // TypeScript calling: Ready for some action? const result = groupBy([{id: 1, value: 'A'}, {id: 1, value: 'B'}, {id: 2, value: 'C'}], 'id');

With TypeScript, type safety in your groupBy function beefs up code quality and squashes potential errors.

Getting the job done: More use cases

Versatile criteria

Ever wanted to customize the grouping criteria? Say no more:

const groupByFunction = (arr, fn) => arr.reduce((acc, item) => { const key = fn(item); // Custom groups for custom folks! (acc[key] = acc[key] || []).push(item); return acc; }, {}); // Using it: Gone are the days of basic groupings... const groupedByCustomLogic = groupByFunction(objects, (obj) => obj.date.getFullYear());

With this version of groupBy, the grouping criteria are set by any function you choose.

Deep dive into nested properties

Puzzled by nested properties? Dive deep with this super handy function:

const groupByDeep = (arr, propPath) => { return arr.reduce((acc, item) => { const keys = propPath.split('.'); const key = keys.reduce((o, k) => (o || {})[k], item); // Grouped deep... like thoughts at 3 AM (acc[key] = acc[key] || []).push(item); return acc; }, {}); };

This version of groupBy ploughs through properties several levels deep to achieve precise grouping.