using lodash .groupBy. how to add your own keys for grouped output?

JavascriptSortingunderscore.jsGroupingLodash

Javascript Problem Overview


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?

Javascript Solutions


Solution 1 - Javascript

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()

Solution 2 - Javascript

Isn't it this simple?

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

Solution 3 - Javascript

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.

Solution 4 - Javascript

another way

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

Solution 5 - Javascript

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/

Solution 6 - Javascript

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));

Solution 7 - Javascript

Here is an updated version using lodash 4 and ES6

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

Solution 8 - Javascript

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);

Solution 9 - Javascript

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.

Solution 10 - Javascript

In 2017 do so

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

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questionuser1767105View Question on Stackoverflow
Solution 1 - JavascriptthefourtheyeView Answer on Stackoverflow
Solution 2 - JavascriptDarraghView Answer on Stackoverflow
Solution 3 - JavascriptPawelView Answer on Stackoverflow
Solution 4 - JavascriptstasovlasView Answer on Stackoverflow
Solution 5 - JavascriptAndrewView Answer on Stackoverflow
Solution 6 - JavascriptNguyễn Văn PhongView Answer on Stackoverflow
Solution 7 - JavascriptmatthewView Answer on Stackoverflow
Solution 8 - JavascriptIyyappan AmirthalingamView Answer on Stackoverflow
Solution 9 - JavascriptelclanrsView Answer on Stackoverflow
Solution 10 - JavascriptВладислав СироштанView Answer on Stackoverflow