How to group arrays and objects in JavaScript using Lodash _.groupBy(). Copy-paste examples for grouping by property, multiple keys, array of objects, nested values, custom functions, and native alternatives.

Lodash _.groupBy() is a JavaScript utility function that takes a collection (array or object) and groups its elements into an object where the keys are determined by a given criterion and the values are arrays of elements that match that key. It returns a plain object — not a Map or array.
// Quick syntax
_.groupBy(collection, iteratee)
Here is a quick example:
const users = [
{ name: "Alice", department: "Engineering" },
{ name: "Bob", department: "Marketing" },
{ name: "Charlie", department: "Engineering" },
{ name: "Diana", department: "Marketing" }
];
const grouped = _.groupBy(users, 'department');
// Result:
// {
// Engineering: [{ name: "Alice", ... }, { name: "Charlie", ... }],
// Marketing: [{ name: "Bob", ... }, { name: "Diana", ... }]
// }
Each element is placed into the group that matches its department value. The original array is not modified.
In this guide, you will learn how to use _.groupBy() to group arrays and objects by property, multiple keys, custom functions, nested values, and computed values. You will also learn how to combine _.groupBy() with other Lodash methods for aggregation and how to achieve the same result with native JavaScript.
🔍 Also check out our guides on Lodash Merge and Lodash SortBy for more data manipulation techniques.
Install the full library:
npm install lodash
Import _.groupBy() in your code:
// ES6 — import only groupBy (tree-shakable)
import groupBy from 'lodash/groupBy';
// CommonJS
const groupBy = require('lodash/groupBy');
// Or import the full library
import _ from 'lodash';
// then use _.groupBy()
Lightweight alternative — install only the groupBy module:
npm install lodash.groupby
import groupBy from 'lodash.groupby';
> Tip: The full Lodash library is ~70KB (minified + gzipped). Importing lodash/groupBy or installing lodash.groupby keeps your bundle lean at ~2KB.
If you are using Lodash in the browser:
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.min.js"></script>
<script>
const result = _.groupBy([6.1, 4.2, 6.3], Math.floor);
// { '4': [4.2], '6': [6.1, 6.3] }
</script>
Here is the complete syntax:
_.groupBy(collection, [iteratee=_.identity])
| Parameter | Type | Description |
|---|---|---|
collection |
Array or Object | The collection to group. |
[iteratee] |
Function or String | The criterion for grouping. Can be a property name (string) or a function that returns the group key. |
| Returns | Object | An object where keys are group names and values are arrays of grouped elements. |
Important behaviors:
(value)undefined, it creates a key called "undefined"_.groupBy() does not mutate the original collectionThe simplest use case — grouping values using a function:
// Group numbers by their floor value
const grouped = _.groupBy([6.1, 4.2, 6.3], Math.floor);
console.log(grouped);
// Output: { '4': [4.2], '6': [6.1, 6.3] }
// Group strings by their first letter
const words = ["apple", "avocado", "banana", "blueberry", "cherry"];
const grouped = _.groupBy(words, (word) => word[0]);
console.log(grouped);
// Output:
// {
// a: ["apple", "avocado"],
// b: ["banana", "blueberry"],
// c: ["cherry"]
// }
// Group numbers by even/odd
const numbers = [1, 2, 3, 4, 5, 6, 7, 8];
const grouped = _.groupBy(numbers, (n) => n % 2 === 0 ? 'even' : 'odd');
console.log(grouped);
// Output: { odd: [1, 3, 5, 7], even: [2, 4, 6, 8] }
This is the most common use case. Pass a property name as a string to group objects by that property:
const employees = [
{ name: "Alice", department: "Engineering", salary: 95000 },
{ name: "Bob", department: "Marketing", salary: 75000 },
{ name: "Charlie", department: "Engineering", salary: 105000 },
{ name: "Diana", department: "Marketing", salary: 80000 },
{ name: "Eve", department: "Design", salary: 85000 }
];
const byDepartment = _.groupBy(employees, 'department');
console.log(byDepartment);
// Output:
// {
// Engineering: [
// { name: "Alice", department: "Engineering", salary: 95000 },
// { name: "Charlie", department: "Engineering", salary: 105000 }
// ],
// Marketing: [
// { name: "Bob", department: "Marketing", salary: 75000 },
// { name: "Diana", department: "Marketing", salary: 80000 }
// ],
// Design: [
// { name: "Eve", department: "Design", salary: 85000 }
// ]
// }
Each employee object is placed intact into its department group. You can then work with each group independently.
// Get all engineers
const engineers = byDepartment['Engineering'];
// [{ name: "Alice", ... }, { name: "Charlie", ... }]
// Count per department
const counts = _.mapValues(byDepartment, (group) => group.length);
// { Engineering: 2, Marketing: 2, Design: 1 }
// Get all department names
const departments = Object.keys(byDepartment);
// ["Engineering", "Marketing", "Design"]
For more complex grouping logic, pass a function instead of a property name:
const products = [
{ name: "Budget Phone", price: 199 },
{ name: "Mid-Range Laptop", price: 799 },
{ name: "Premium Tablet", price: 1299 },
{ name: "Budget Earbuds", price: 49 },
{ name: "Mid-Range Monitor", price: 449 }
];
const byPriceRange = _.groupBy(products, (product) => {
if (product.price < 100) return 'budget';
if (product.price < 500) return 'mid-range';
if (product.price < 1000) return 'premium';
return 'luxury';
});
console.log(byPriceRange);
// Output:
// {
// budget: [{ name: "Budget Earbuds", price: 49 }],
// "mid-range": [{ name: "Budget Phone", price: 199 }, { name: "Mid-Range Monitor", price: 449 }],
// premium: [{ name: "Mid-Range Laptop", price: 799 }],
// luxury: [{ name: "Premium Tablet", price: 1299 }]
// }
const orders = [
{ id: 1, date: "2025-01-15", total: 99 },
{ id: 2, date: "2025-01-20", total: 149 },
{ id: 3, date: "2025-02-05", total: 79 },
{ id: 4, date: "2025-02-18", total: 199 },
{ id: 5, date: "2025-03-01", total: 59 }
];
// Group by month
const byMonth = _.groupBy(orders, (order) => order.date.substring(0, 7));
console.log(byMonth);
// Output:
// {
// "2025-01": [{ id: 1, ... }, { id: 2, ... }],
// "2025-02": [{ id: 3, ... }, { id: 4, ... }],
// "2025-03": [{ id: 5, ... }]
// }
const tasks = [
{ title: "Write docs", completed: true },
{ title: "Fix bug", completed: false },
{ title: "Deploy", completed: true },
{ title: "Code review", completed: false }
];
const grouped = _.groupBy(tasks, 'completed');
console.log(grouped);
// Output:
// {
// true: [{ title: "Write docs", ... }, { title: "Deploy", ... }],
// false: [{ title: "Fix bug", ... }, { title: "Code review", ... }]
// }
// Count completed vs pending
console.log(`Done: ${grouped['true'].length}, Pending: ${grouped['false'].length}`);
// Output: "Done: 2, Pending: 2"
_.groupBy() natively supports only one key. To group by multiple keys, you have several approaches:
Concatenate multiple properties into a single key:
const sales = [
{ product: "Widget", region: "North", amount: 100 },
{ product: "Widget", region: "South", amount: 200 },
{ product: "Gadget", region: "North", amount: 150 },
{ product: "Widget", region: "North", amount: 300 },
{ product: "Gadget", region: "South", amount: 250 }
];
const grouped = _.groupBy(sales, (item) => `${item.product}_${item.region}`);
console.log(grouped);
// Output:
// {
// "Widget_North": [{ product: "Widget", region: "North", amount: 100 }, { ... amount: 300 }],
// "Widget_South": [{ product: "Widget", region: "South", amount: 200 }],
// "Gadget_North": [{ product: "Gadget", region: "North", amount: 150 }],
// "Gadget_South": [{ product: "Gadget", region: "South", amount: 250 }]
// }
Group by the first key, then group each group by the second key:
const byProductThenRegion = _.mapValues(
_.groupBy(sales, 'product'),
(productGroup) => _.groupBy(productGroup, 'region')
);
console.log(byProductThenRegion);
// Output:
// {
// Widget: {
// North: [{ product: "Widget", region: "North", amount: 100 }, { ... amount: 300 }],
// South: [{ product: "Widget", region: "South", amount: 200 }]
// },
// Gadget: {
// North: [{ product: "Gadget", region: "North", amount: 150 }],
// South: [{ product: "Gadget", region: "South", amount: 250 }]
// }
// }
For more than two keys, serialize as JSON:
const grouped = _.groupBy(sales, (item) =>
JSON.stringify({ product: item.product, region: item.region })
);
// Access a specific group:
const widgetNorth = grouped[JSON.stringify({ product: "Widget", region: "North" })];
To group by a nested property, use a function to access the nested value:
const employees = [
{ name: "Alice", address: { city: "Kolkata", state: "WB" } },
{ name: "Bob", address: { city: "Mumbai", state: "MH" } },
{ name: "Charlie", address: { city: "Pune", state: "MH" } },
{ name: "Diana", address: { city: "Howrah", state: "WB" } }
];
const byState = _.groupBy(employees, (emp) => emp.address.state);
console.log(byState);
// Output:
// {
// WB: [{ name: "Alice", ... }, { name: "Diana", ... }],
// MH: [{ name: "Bob", ... }, { name: "Charlie", ... }]
// }
> Note: Unlike _.sortBy(), you cannot use dot notation strings like 'address.state' with _.groupBy(). You must use a function.
The real power of _.groupBy() comes from combining it with other Lodash utilities:
const expenses = [
{ category: "Food", amount: 50 },
{ category: "Transport", amount: 30 },
{ category: "Food", amount: 65 },
{ category: "Entertainment", amount: 100 },
{ category: "Transport", amount: 45 }
];
const grouped = _.groupBy(expenses, 'category');
const totals = _.mapValues(grouped, (items) => _.sumBy(items, 'amount'));
console.log(totals);
// Output: { Food: 115, Transport: 75, Entertainment: 100 }
const orders = [
{ customer: "Alice", item: "Laptop", price: 999 },
{ customer: "Alice", item: "Mouse", price: 29 },
{ customer: "Bob", item: "Keyboard", price: 79 }
];
const grouped = _.groupBy(orders, 'customer');
const summary = _.mapValues(grouped, (orders) => ({
orderCount: orders.length,
totalSpent: _.sumBy(orders, 'price'),
items: orders.map(o => o.item)
}));
console.log(summary);
// Output:
// {
// Alice: { orderCount: 2, totalSpent: 1028, items: ["Laptop", "Mouse"] },
// Bob: { orderCount: 1, totalSpent: 79, items: ["Keyboard"] }
// }
const students = [
{ name: "Alice", subject: "Math", score: 95 },
{ name: "Bob", subject: "Math", score: 88 },
{ name: "Charlie", subject: "Science", score: 92 },
{ name: "Diana", subject: "Science", score: 97 }
];
const grouped = _.groupBy(students, 'subject');
const topStudents = _.mapValues(grouped, (group) => _.maxBy(group, 'score').name);
console.log(topStudents);
// Output: { Math: "Alice", Science: "Diana" }
Need to sort items within each group? Combine _.groupBy() with _.sortBy():
const products = [
{ name: "Widget C", category: "Tools", price: 30 },
{ name: "Widget A", category: "Tools", price: 10 },
{ name: "Gadget B", category: "Electronics", price: 50 },
{ name: "Gadget A", category: "Electronics", price: 20 }
];
const grouped = _.groupBy(products, 'category');
const sortedGroups = _.mapValues(grouped, (items) => _.sortBy(items, 'price'));
// Each category's products are now sorted by price
When a property value is undefined or null, _.groupBy() converts it to a string key:
const items = [
{ name: "A", category: "Food" },
{ name: "B", category: undefined },
{ name: "C", category: null },
{ name: "D", category: "Food" }
];
const grouped = _.groupBy(items, 'category');
console.log(grouped);
// Output:
// {
// Food: [{ name: "A", ... }, { name: "D", ... }],
// undefined: [{ name: "B", ... }],
// null: [{ name: "C", ... }]
// }
To handle this gracefully, provide a default in the iteratee:
const grouped = _.groupBy(items, (item) => item.category || 'Uncategorized');
// Now undefined and null items go into "Uncategorized"
const grouped = _.groupBy([], 'category');
console.log(grouped);
// Output: {}
_.groupBy() returns an empty object for empty collections — no errors.
JavaScript now has a native Object.groupBy() method (Stage 4, available in Node 21+ and modern browsers):
// Native (ES2024)
const grouped = Object.groupBy(users, (user) => user.department);
// Lodash
const grouped = _.groupBy(users, 'department');
For environments without Lodash or Object.groupBy():
// Native reduce
const grouped = users.reduce((acc, user) => {
const key = user.department;
if (!acc[key]) acc[key] = [];
acc[key].push(user);
return acc;
}, {});
// Lodash — same result, cleaner syntax
const grouped = _.groupBy(users, 'department');
| Feature | _.groupBy() |
Object.groupBy() |
Array.reduce() |
|---|---|---|---|
| Syntax | _.groupBy(arr, 'key') |
Object.groupBy(arr, fn) |
Manual reduce logic |
| String shorthand | ✅ 'property' |
❌ Needs function | ❌ Needs function |
| Browser support | All (via npm) | ES2024+ / Node 21+ | All |
| Bundle size | ~2KB | 0KB (native) | 0KB (native) |
| Chaining with Lodash | ✅ Seamless | ❌ Manual | ❌ Manual |
When to use _.groupBy(): Your project uses Lodash, you need the string shorthand, or you need to support older browsers.
When to use native: You target modern browsers/Node, want zero dependencies, or the grouping logic is simple.
const products = [
{ id: 1, name: "Laptop", category: "Electronics", price: 999 },
{ id: 2, name: "T-Shirt", category: "Clothing", price: 29 },
{ id: 3, name: "Headphones", category: "Electronics", price: 199 },
{ id: 4, name: "Jeans", category: "Clothing", price: 59 },
{ id: 5, name: "Novel", category: "Books", price: 15 }
];
const catalog = _.groupBy(products, 'category');
// Render a category sidebar:
Object.entries(catalog).forEach(([category, items]) => {
console.log(`${category} (${items.length} products)`);
});
// Electronics (2 products)
// Clothing (2 products)
// Books (1 products)
const pageViews = [
{ url: "/home", timestamp: "2025-02-01T10:00:00Z" },
{ url: "/about", timestamp: "2025-02-01T14:30:00Z" },
{ url: "/home", timestamp: "2025-02-02T09:15:00Z" },
{ url: "/contact", timestamp: "2025-02-02T16:45:00Z" }
];
const byDate = _.groupBy(pageViews, (view) => view.timestamp.split('T')[0]);
const dailyCount = _.mapValues(byDate, (views) => views.length);
console.log(dailyCount);
// { "2025-02-01": 2, "2025-02-02": 2 }
const apiResponse = [
{ id: 1, status: "success", data: "..." },
{ id: 2, status: "error", message: "Not found" },
{ id: 3, status: "success", data: "..." },
{ id: 4, status: "error", message: "Timeout" }
];
const { success = [], error = [] } = _.groupBy(apiResponse, 'status');
console.log(`${success.length} succeeded, ${error.length} failed`);
// "2 succeeded, 2 failed"
// Handle errors
error.forEach(e => console.error(`Request ${e.id}: ${e.message}`));
> Tip: If your API returns partial data across multiple requests, use _.merge() to deep merge responses before grouping them.
_.groupBy() iterates through the collection once:
Tips:
_.groupBy() is highly efficient for its purpose — a single pass O(n) operation// ✅ Efficient — groups and transforms in two passes
const result = _.mapValues(_.groupBy(data, 'category'), (items) => items.length);
// ❌ Inefficient — groups, then iterates groups, then iterates each group
const grouped = _.groupBy(data, 'category');
const result = {};
for (const key in grouped) {
result[key] = 0;
for (const item of grouped[key]) {
result[key]++;
}
}
_.mergeWith(), and avoid prototype pollution._.orderBy(), and case-insensitive sorting._.groupBy() is a Lodash function that takes a collection (array or object) and a criterion (property name or function), then returns an object where keys are group names and values are arrays of elements matching that key. It iterates through the collection once, placing each element into the appropriate group based on the iteratee's return value.
_.groupBy() natively supports only one key. For multiple keys, you can create a composite key string (_.groupBy(arr, (x) => x.key1 + '_' + x.key2)), use nested groupBy calls (_.mapValues(_.groupBy(arr, 'key1'), (g) => _.groupBy(g, 'key2'))), or serialize keys as JSON.
Yes. When a grouping key is undefined or null, _.groupBy() creates string keys "undefined" or "null" respectively. To handle these gracefully, use a function with a default: _.groupBy(arr, (x) => x.category || 'Uncategorized').
_.groupBy() splits a collection into multiple groups based on any criterion. _.partition() splits a collection into exactly two groups — items that match a predicate and items that don't. Use _.partition() when you only need a true/false split.
Yes. Object.groupBy() was introduced in ES2024 and is available in Node 21+ and modern browsers. For older environments, Array.prototype.reduce() can achieve the same result. However, _.groupBy() offers cleaner syntax with string property shorthands and integrates seamlessly with other Lodash utilities.
No. _.groupBy() returns a new object containing references to the original elements. The original collection is never modified. However, the elements inside each group are the same references — not deep copies — so modifying an element in a group will also modify it in the original array.
Comments
Sign in to join the discussion.
No comments yet. Be the first to share your thoughts.