155

I have this sample data returned from an API.

I'm using Lodash's _.groupBy to convert the data into an object I can use better. The raw data returned is this:

[ { "name": "jim", "color": "blue", "age": "22" }, { "name": "Sam", "color": "blue", "age": "33" }, { "name": "eddie", "color": "green", "age": "77" } ] 

I want the _.groupBy function to return an object that looks like this:

[ { color: "blue", users: [ { "name": "jim", "color": "blue", "age": "22" }, { "name": "Sam", "color": "blue", "age": "33" } ] }, { color: "green", users: [ { "name": "eddie", "color": "green", "age": "77" } ] } ] 

Currently I'm using

_.groupBy(a, function(b) { return b.color}) 

which is returning this.

{blue: [{..}], green: [{...}]} 

the groupings are correct, but I'd really like to add the keys I want (color, users). is this possible using _.groupBy? or some other LoDash utility?

1
  • As of 2024, there is no need to use lodash, underscore.js or alikey anymore, since native groupBy() is now available. Commented Feb 14, 2024 at 23:28

12 Answers 12

229

You can do it like this in both Underscore and Lodash (3.x and 4.x).

var data = [{ "name": "jim", "color": "blue", "age": "22" }, { "name": "Sam", "color": "blue", "age": "33" }, { "name": "eddie", "color": "green", "age": "77" }]; console.log( _.chain(data) // Group the elements of Array based on `color` property .groupBy("color") // `key` is group's name (color), `value` is the array of objects .map((value, key) => ({ color: key, users: value })) .value() );
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.min.js"></script>


Original Answer

var result = _.chain(data) .groupBy("color") .pairs() .map(function(currentItem) { return _.object(_.zip(["color", "users"], currentItem)); }) .value(); console.log(result); 

Online Demo

Note: Lodash 4.0 onwards, the .pairs function has been renamed to _.toPairs()

Sign up to request clarification or add additional context in comments.

9 Comments

Very nifty, but hard to wrap my head around. Can you explain the steps in between, especially the pairing and zipping (and the double zip, since _.object is an alias for _.zipObject).
lodash 3.10.0 and some logging for every step: jsfiddle.net/plantface/WYCF8/171. It's still a puzzle, but I'm getting there. Haven't used _.zip and _.pair so much yet.
In lodash 4.x, the pairs() method does not exist anymore. An updated lodash 4.0 version of this example is: .chain(data).groupBy('color') .toPairs().map(function (pair) { return _.zipObject(['Name', 'Suppliers'], air); }).value();
I feel like loadash should have a function that does this very exact thing. I would think users would want to group an array of objects by a property all the time.
Using _.chain is considered a bad practice now.
|
128

Isn't it this simple?

var result = _(data) .groupBy(x => x.color) .map((value, key) => ({color: key, users: value})) .value(); 

3 Comments

This seems much cleaner to me, and appears to be returning the same results. Thanks!
wow. what syntax is this? First time seeing that you can pass the array into the constructor
Watch out for this: stackoverflow.com/questions/56389864/… if you don't want to import the entire library. I think that means 'flow' is better despite this looking really nice!
31

Highest voted answer uses Lodash _.chain function which is considered a bad practice now "Why using _.chain is a mistake."

Here is a fewliner that approaches the problem from functional programming perspective:

import tap from "lodash/fp/tap"; import flow from "lodash/fp/flow"; import groupBy from "lodash/fp/groupBy"; const map = require('lodash/fp/map').convert({ 'cap': false }); const result = flow( groupBy('color'), map((users, color) => ({color, users})), tap(console.log) )(input) 

Where input is an array that you want to convert.

3 Comments

I tried to use flow() instead of chain() here, but typescript's type checking simply go crazy with composed functions. I hope we have the same level of support we currently have in RxJS's pipe(), for example, in lodash.
I really like your map() syntax, very cool map((users, color) => ({color, users})) instead of map((value, key) => ({ color: key, users: value }))
The problem of preventing treeshaking only applies to Lodash. In Underscore, you can customize which functions get mixed into _ since version 1.11.
26

another way

_.chain(data) .groupBy('color') .map((users, color) => ({ users, color })) .value(); 

1 Comment

Similar to @thefourtheye's answer but a bit easier to understand
8

Thanks @thefourtheye, your code greatly helped. I created a generic function from your solution using the version 4.5.0 of Lodash.

function groupBy(dataToGroupOn, fieldNameToGroupOn, fieldNameForGroupName, fieldNameForChildren) { var result = _.chain(dataToGroupOn) .groupBy(fieldNameToGroupOn) .toPairs() .map(function (currentItem) { return _.zipObject([fieldNameForGroupName, fieldNameForChildren], currentItem); }) .value(); return result; } 

To use it:

var result = groupBy(data, 'color', 'colorId', 'users'); 

Here is the updated fiddler;

https://jsfiddle.net/sc2L9dby/

Comments

6

Other answers look good by using lodash.

And I would suggest a different approach by using JavaScript, such as Array#reduce, object for storing and ??= syntax. As a result, it just takes O(n) time complexity.

const data = [{ "name": "jim", "color": "blue", "age": "22" }, { "name": "Sam", "color": "blue", "age": "33" }, { "name": "eddie", "color": "green", "age": "77" }]; const result = data.reduce((acc, { name, color, age }) => { acc[color] ??= { color: color, users: [] }; acc[color].users.push({ name, color, age }); return acc; }, {}); console.log(Object.values(result));

Comments

6

ES2024

You can now use the built-in Object.groupBy() or Map.groupBy() in conjunction with the good old map(). No need for lodash, underscore.js or alike anymore:

const input = [ { name: "jim", color: "blue", age: "22", }, { name: "Sam", color: "blue", age: "33", }, { name: "eddie", color: "green", age: "77", }, ]; const usersByColor = Object.groupBy(input, (user) => user.color); const result = Object.entries(usersByColor).map(([color, users]) => ({ color, users, })); console.log(JSON.stringify(result, null, 4));
/* Stackoverflow: show only console */ .as-console-wrapper { max-height: 100% !important; top: 0; }

Browser support is ok (~ 82% as of 2024-02-15) but not great yet.

2 Comments

Awesome, did not know this one!
This should've been there 20 years ago :-(
5

Here is an updated version using lodash 4 and ES6

const result = _.chain(data) .groupBy("color") .toPairs() .map(pair => _.zipObject(['color', 'users'], pair)) .value(); 

Comments

3

Example groupBy and sum of a column using Lodash 4.17.4

 var data = [{ "name": "jim", "color": "blue", "amount": 22 }, { "name": "Sam", "color": "blue", "amount": 33 }, { "name": "eddie", "color": "green", "amount": 77 }]; var result = _(data) .groupBy(x => x.color) .map((value, key) => ({color: key, totalamount: _.sumBy(value,'amount'), users: value})).value(); console.log(result); 

Comments

2

I would suggest a different approach, using my own library you could do this in a few lines:

var groupMe = sequence( groupBy(pluck('color')), forOwn(function(acc, k, v) { acc.push({colors: k, users: v}); return acc; },[]) ); var result = groupMe(collection); 

This would a be a bit difficult with lodash or Underscore because the arguments are in the opposite order order, so you'd have to use _.partial a lot.

Comments

0

 const groupBy = (array, key, name) => { return array.reduce((result, obj) => { result[obj[key]] = result[obj[key]] || { location: obj[name], child: [], }; // result[obj[key]] ??= { // location: obj[name], // child: [] // }; result[obj[key]].child.push(obj); return result; }, {}); }; const items = [ { id: 1, age: 75, name: 'Kumar', location: 'chennai', }, { id: 1, age: 25, name: 'Christo', location: 'Dindigul', }, { id: 1, age: 28, name: 'SK', location: 'chennai', }, { id: 1, age: 21, name: 'Ram', location: 'chennai', }, { id: 1, age: 21, name: 'ravi', location: 'chennai', }, ]; let obj = groupBy(items, 'age', 'location'); const result = Object.keys(obj).map((key) => [obj[key]][0]); console.log(result); 

1 Comment

Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.
-2

In 2017 do so

_.chain(data) .groupBy("color") .toPairs() .map(item => _.zipObject(["color", "users"], item)) .value(); 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.