Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Group Array of JavaScript Objects by Key or Property Value

Group Array of JavaScript Objects by Key or Property Value

Implementation

const groupBy = key => array =>
  array.reduce((objectsByKeyValue, obj) => {
    const value = obj[key];
    objectsByKeyValue[value] = (objectsByKeyValue[value] || []).concat(obj);
    return objectsByKeyValue;
  }, {});

Or using an implicit return (slower):

const groupBy = key => array =>
  array.reduce(
    (objectsByKeyValue, obj) => ({
      ...objectsByKeyValue,
      [obj[key]]: (objectsByKeyValue[obj[key]] || []).concat(obj)
    }),
    {}
  );

Usage

const cars = [
  { brand: 'Audi', color: 'black' },
  { brand: 'Audi', color: 'white' },
  { brand: 'Ferarri', color: 'red' },
  { brand: 'Ford', color: 'white' },
  { brand: 'Peugot', color: 'white' }
];

const groupByBrand = groupBy('brand');
const groupByColor = groupBy('color');

console.log(
  JSON.stringify({
    carsByBrand: groupByBrand(cars),
    carsByColor: groupByColor(cars)
  }, null, 2)
);

Output

{
  "carsByBrand": {
    "Audi": [
      {
        "brand": "Audi",
        "color": "black"
      },
      {
        "brand": "Audi",
        "color": "white"
      }
    ],
    "Ferarri": [
      {
        "brand": "Ferarri",
        "color": "red"
      }
    ],
    "Ford": [
      {
        "brand": "Ford",
        "color": "white"
      }
    ],
    "Peugot": [
      {
        "brand": "Peugot",
        "color": "white"
      }
    ]
  },
  "carsByColor": {
    "black": [
      {
        "brand": "Audi",
        "color": "black"
      }
    ],
    "white": [
      {
        "brand": "Audi",
        "color": "white"
      },
      {
        "brand": "Ford",
        "color": "white"
      },
      {
        "brand": "Peugot",
        "color": "white"
      }
    ],
    "red": [
      {
        "brand": "Ferarri",
        "color": "red"
      }
    ]
  }
}
@MoonSeeing

This comment has been minimized.

Copy link

MoonSeeing commented May 21, 2019

thanks . your code help me solve my problem

@senthil-developer

This comment has been minimized.

Copy link

senthil-developer commented Jun 4, 2019

Great Solution!

@mikaello

This comment has been minimized.

Copy link

mikaello commented Jun 6, 2019

Nice! Works very well. I adjusted the groupBy function slightly to accept an array of keys instead of just a single key, so it is possible to group by multiple properties: https://gist.github.com/mikaello/06a76bca33e5d79cdd80c162d7774e9c

@JamieMason

This comment has been minimized.

Copy link
Owner Author

JamieMason commented Jun 6, 2019

Nice @mikaello, that's really good! Very useful 👍

@shuttlehawk

This comment has been minimized.

Copy link

shuttlehawk commented Jun 11, 2019

This is brilliant! Thanks!

I'm working through a situation where I'd want something like the following.

Audi
    black
    white
Ferrari
    red
Ford
    white
Peugot
   white

Is the following a decent approach to this or am I over-complicating?

for (var commonBrand in carsByBrand){
    console.log(commonBrand)
    for(var i = 0; i < carsByBrand[commonBrand].length; i++){
        console.log("    " + carsByBrand[commonBrand][i].color)
    }
}
@JamieMason

This comment has been minimized.

Copy link
Owner Author

JamieMason commented Jun 13, 2019

That looks fine to me @shuttlehawk, not over-complicated 👍

Just for fun, if you were to do the same using Array and Object functions, the equivalent would look like this:

Object.keys(carsByBrand).forEach((commonBrand) => {
  console.log(commonBrand);
  commonBrand.forEach((car) => {
    console.log('    ' + car.color);
  });
});
@shuttlehawk

This comment has been minimized.

Copy link

shuttlehawk commented Jun 26, 2019

Thanks for the feedback. I'm going to use your equivalent so I can do a little learning here, plus it looks way cooler! haha!

@nerfpops

This comment has been minimized.

Copy link

nerfpops commented Jul 18, 2019

Thanks for sharing the code. Here's a TypeScript annotated version

function groupBy<T extends any, K extends keyof T>(array: T[], key: K): Record<T[K], T[]> {
  return array.reduce(
    (objectsByKeyValue, obj) => {
      const value = obj[key]
      objectsByKeyValue[value] = (objectsByKeyValue[value] || []).concat(obj)
      return objectsByKeyValue
    },
    {} as Record<T[K], T[]>
  )
}
@alexandrzavalii

This comment has been minimized.

Copy link

alexandrzavalii commented Aug 15, 2019

How would you group by nested field?
For example by field "value"

const cars = [
  { brand: 'Audi', color: { value: 'black' } },
  { brand: 'Audi', color: { value: 'white' } },
];
@nerfpops

This comment has been minimized.

Copy link

nerfpops commented Aug 15, 2019

Either flatten the objects first, like { brand: 'Audi', color_value: 'black' } or pass a function taking each object in the array, returning the desired value on that object. Eg. instead of const value = obj[key] do const value = keyFn(obj). Another approach would be to pass a key with dots, like 'color.value' and have the function parse that. Now the TypeScript required for the last example is excessive... microsoft/TypeScript#12290

@nerfpops

This comment has been minimized.

Copy link

nerfpops commented Aug 15, 2019

Here's a way to do it in TypeScript:

function groupBy<T extends any, K extends keyof T>(array: T[], key: K | { (obj: T): string }): Record<string, T[]> {
  const keyFn = key instanceof Function ? key : (obj: T) => obj[key]
  return array.reduce(
    (objectsByKeyValue, obj) => {
      const value = keyFn(obj)
      objectsByKeyValue[value] = (objectsByKeyValue[value] || []).concat(obj)
      return objectsByKeyValue
    },
    {} as Record<string, T[]>
  )
}

let arr = [
    { test: "abc", a: 1, color: { value: "black" } },
    { test: "abc", a: 2, color: { value: "black" } },
    { test: "def", a: 1, color: { value: "white" } }
]

let result = groupBy(arr, obj => obj.color.value)

console.log(JSON.stringify(result, null, 2))

http://www.typescriptlang.org/play/#code/GYVwdgxgLglg9mABAcwE5xABwEIE8A8AKogKYAeUJYAJgM6ICGYuANIgNKkVV2IDWJXHGCJCAPgAUDVKga4AXKIDaAXTYCFHRAB9EAb0QS4AIwBWiwgEpFtKKhhhkiAL7XEAJRIQ4qavlv2jmyEqmL6AFCIiN5gtvyCAGJIALzxuIgOtkwQJMKICeDQ8EgA-GmIikZmFpaIyWEmpkoaKpGIqCRQIKhI0rK4AHQd1CA5Em1RVaZeULR47IIAagwANiAkbI219RFRe9EIcQBuq+t1aUlTlhN7jTNzuAu4y2skSievKudT9-NLp28PusvtpdKpLAMYhAGFArjcoh0uj1EHdoA8ni91jdnCwbnpnIx6J5vL5-HYHMhgqE2tdnOFwitOowZOclHjEJRbIoAEQMYwQblsBiKACMbG8Kx8igMQJIPOMKwYED43JcLlx+wMnKgPL5AqFigATOK4JLUNLELL5YrlaqCTj2dqedQSMBBYxRSazRarYhuQB3AAWMEodpc4VaDKZHVoIBWUHOaAwOFwUhkmzMdQaZkhpp8A1l13CMVoppIA0lyAkACkAMoAeQAcgMAhSYMBUzG41A2GA4ys2IbLJYgA

@selvanradha

This comment has been minimized.

Copy link

selvanradha commented Sep 4, 2019

very nice to see! Thanks! great work man

@adc91

This comment has been minimized.

Copy link

adc91 commented Sep 20, 2019

Nice!! Thanks

@hspeight

This comment has been minimized.

Copy link

hspeight commented Sep 24, 2019

Worked perfectly for me and very easy to implement.
Much appreciated.
Thanks!

@Sanyug92

This comment has been minimized.

Copy link

Sanyug92 commented Oct 6, 2019

Works perfectly! Thanks!

@SimiCode

This comment has been minimized.

Copy link

SimiCode commented Oct 8, 2019

Thanks!

@nkhil

This comment has been minimized.

Copy link

nkhil commented Oct 28, 2019

Thanks so much @JamieMason !

@daraxdray

This comment has been minimized.

Copy link

daraxdray commented Nov 18, 2019

Thank you greatly....

@SnehaAitha

This comment has been minimized.

Copy link

SnehaAitha commented Dec 19, 2019

I am trying to use the group by function on a JSON array using the inner JSON value as a key as shown below. But unable to read the inner JSON value. Here is my JSON array.

NotificationData = [
{
"eventId":"90989",
"eventTime":"2019-12-11T11:20:53+04:00",
"eventType":"yyyy",
"event":{
"ServiceOrder":{
"externalId":"2434",
"priority":"1"
}
}
},
{
"eventId":"6576",
"eventTime":"2019-12-11T11:20:53+04:00",
"eventType":"yyyy",
"event":{
"ServiceOrder":{
"externalId":"78657",
"priority":"1"
}
}
}
]

// GroupBy Logic used:

const groupBy = (array, key) => {
return array.reduce((result, currentValue) => {
(result[currentValue[key]] = result[currentValue[key]] || []).push(
currentValue
);
return result;
}, {});
};

const serviceOrdersGroupedByExternalId = groupBy(this.NotificationData, 'event.ServiceOrder.externalId');//this line of code is not working as it is unable to locate the external id value.

Desired Output:

{
"2434":[
{
"eventId":"90989",
"eventTime":"2019-12-11T11:20:53+04:00",
"eventType":"yyyy",
"event":{
"ServiceOrder":{
"priority":"1"
}
}
}
],
"78657":[
{
"eventId":"6576",
"eventTime":"2019-12-11T11:20:53+04:00",
"eventType":"yyyy",
"event":{
"ServiceOrder":{
"priority":"1"
}
}
}
]
}

Kindly suggest me a way to do this.

@senthil-developer

This comment has been minimized.

Copy link

senthil-developer commented Dec 19, 2019

@SnehaAitha, here is my suggestion.

  // GroupBy Logic:
  const groupBy = (array, key) => {
    return array.reduce((result, currentValue) => {
      // get the nested propert value
      const objKey = nestedObjectByString(currentValue, key);
      result[objKey] = (result[objKey] || []).concat(
        currentValue)
      return result;
    }, {});
  };

  // return value of nested property of an object
  const nestedObjectByString = (obj, key) => {
    key = key.replace(/\[(\w+)\]/g, '.$1');  // convert indexes to properties
    key = key.replace(/^\./, ''); // strip a leading dot
    const a = key.split('.');
    for (let i = 0, n = a.length; i < n; ++i) {
      const k = a[i];
      if (k in obj) {
        obj = obj[k];
      } else {
        return;
      }
    }
    return obj;
  }
const NotificationData = [
      {
        "eventId": "90989",
        "eventTime": "2019-12-11T11:20:53+04:00",
        "eventType": "yyyy",
        "event": {
          "ServiceOrder": {
            "externalId": "2434",
            "priority": "1"
          }
        }
      },
      {
        "eventId": "6576",
        "eventTime": "2019-12-11T11:20:53+04:00",
        "eventType": "yyyy",
        "event": {
          "ServiceOrder": {
            "externalId": "78657",
            "priority": "1"
          }
        }
      }
    ];
const serviceOrdersGroupedByExternalId = groupBy(NotificationData, 'event.ServiceOrder.externalId');

Let me know if it doesn't work.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.