Explain Codes LogoExplain Codes Logo

How can I group an array of objects by key?

javascript
reduce
groupby
lodash
Nikita BarsukovbyNikita Barsukov·Oct 31, 2024
TLDR

To group objects by key in JavaScript, the reduce() method is often used to assemble items into clusters. Here’s a quick and efficent solution to accomplish this:

// Declare the groupBy function const groupBy = (array, key) => // Reduce the array to an object that accumulates its items array.reduce((acc, obj) => { // Throw in some buckets (or clusters, if you like) (acc[obj[key]] ??= []).push(obj); return acc; // Now go make that happy llama dance! }, {}); // Prepare a simple array of pets for demonstration const pets = [{type: 'dog', name: 'Max'}, {type: 'cat', name: 'Kitty'}, {type: 'dog', name: 'Rex'}]; // Group the pets by their type const groupedByType = groupBy(pets, 'type'); console.log(groupedByType); // Will print an object: {dog: [{...}, {...}], cat: [{...}]}

This groupBy function returns an object that consists of properties representing unique group names (e.g. 'dog', 'cat') and the associated array of objects belonging to that group.

Introducing Reduce

The reduce function is a powerful tool used to condense the array elements into a single cumulative result. It works by traversing the array from left to right and applying a function to each element.

In our case, we are accumulating an object, where each property corresponds to the value of a unique key in our array elements. The value of each property is an array of elements that share the same key.

// Declare the groupBy function const groupBy = (array, key) => // Explanation: I want to go through my array and stuff items // into specific buckets identified by `key`. If the bucket doesn't // exist, I'll make a new one. Sounds good? Let's do this! array.reduce((acc, obj) => { (acc[obj[key]] ??= []).push(obj); return acc; // Return accumulated object to be used in the next iteration }, {}); // Start the reduction with an empty object

Working with absent keys

In a situation where some objects in your array might not contain the key you're grouping by, you can filter out such objects before the reduce operation. This prevents the possibility of trying to access properties from undefined.

const groupBy = (array, key) => array // If an object ain't got my key, it ain't coming to my party! .filter(obj => obj.hasOwnProperty(key)) .reduce((acc, obj) => ((acc[obj[key]] ??= []).push(obj), acc), {});

Simplification courtesy of ES6

Modern JavaScript syntax (ES6+) offers ways to make our code more concise and readable. Leveraging arrow functions, default parameters, and optional chaining, we can make our groupBy function a neat one-liner:

const groupBy = (array, key) => array.reduce((acc, obj) => ((acc[obj[key]] = acc[obj[key]] || []).push(obj), acc), {});

The lodash alternative

If you love external libraries, lodash got you covered with a convenient _.groupBy() function:

const _ = require('lodash'); const groupedByType = _.groupBy(pets, 'type');

Our friend lodash doesn't stop there. If you need to shape-shift the grouped results, _.mapValues() can be very useful to transform the grouped object values:

const transformed = _.mapValues(groupedByType, (objs) => /* your shapeshifting magic here */);

Going deeper: Duplicates, Clean Objects and Merging

Handling duplicates

If your data has reoccuring keys, make sure the final object structure eliminates duplicate keys. You can handle duplicates using either ES6 features like Set or lodash's .omit.

Embrace clean objects

Starting your reduction with an object? Go for Object.create(null) for a fresh, empty object that doesn't inherit properties from Object.prototype.

Uniting grouped objects

Merging grouped objects into one. No problem! You can use Object.assign for that fine piece of object composition.

Practical examples and custom variations

Sorting the groups

After grouping, you can sort the resulted object by its keys:

const groupedSorted = Object.keys(groupedByType) // Perform Array.sort() on keys .sort() // Re-construct the object with sorted keys .reduce((acc, key) => ({ ...acc, [key]: groupedByType[key] }), {}); // Result: Object with sorted keys 🔥

Grouping by multiple keys

Sometimes, you might want to group by multiple keys. Fear not, it's also possible!

const groupByMultiple = (array, ...keys) => array.reduce((acc, obj) => { // We just loop the keys and make sure our objects are in the right bucket keys.reduce((accInner, key) => ((accInner[obj[key]] ??= []).push(obj), accInner), acc); return acc; }, {});

Dynamic property grouping

You can also group by properties that are determined by a dynamic function:

const groupByDynamic = (array, fn) => array.reduce((acc, obj) => { const key = fn(obj); ((acc[key] ??= []).push(obj), acc); return acc; }, {});