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

240,117

Solution 1

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

Isn't it this simple?

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

Solution 3

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

another way

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

Solution 5

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/

Share:
240,117
user1767105
Author by

user1767105

Updated on January 08, 2022

Comments

  • user1767105
    user1767105 over 2 years

    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?

  • Benny Bottema
    Benny Bottema almost 9 years
    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).
  • thefourtheye
    thefourtheye almost 9 years
    Print the result after each step. It might help you understand better. If you have specific question please let me know. I'll help you with that
  • Benny Bottema
    Benny Bottema almost 9 years
    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.
  • Erik Schierboom
    Erik Schierboom over 8 years
    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();
  • Adam Klein
    Adam Klein over 8 years
    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.
  • Pikachu-go
    Pikachu-go over 7 years
    I am getting Object instead of values [ { color: '0', users: [ [Object], [Object] ] } May be a newbie issue - help is greatly appreciated.
  • Martin Magakian
    Martin Magakian over 7 years
    Similar to @thefourtheye's answer but a bit easier to understand
  • Jeremy A. West
    Jeremy A. West about 7 years
    This seems much cleaner to me, and appears to be returning the same results. Thanks!
  • Ben Cochrane
    Ben Cochrane over 6 years
    The updated version to this answer which worked well is: var result = lodash.chain(res) .groupBy('color') .toPairs() .map(function(currentItem) { return lodash.zipObject(['color', 'users'], currentItem) }) .value();
  • Pawel
    Pawel over 6 years
    Using _.chain is considered a bad practice now.
  • Wei-jye
    Wei-jye almost 6 years
    wow. what syntax is this? First time seeing that you can pass the array into the constructor
  • B Medeiros
    B Medeiros over 4 years
    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.
  • Gil Epshtain
    Gil Epshtain about 4 years
    I really like your map() syntax, very cool map((users, color) => ({color, users})) instead of map((value, key) => ({ color: key, users: value }))
  • Julian
    Julian about 3 years
    The problem of preventing treeshaking only applies to Lodash. In Underscore, you can customize which functions get mixed into _ since version 1.11.
  • Mo.
    Mo. over 2 years
    @ErikSchierboom what is air? You mean pair !