Skip to content

Instantly share code, notes, and snippets.

@mikaello
Forked from JamieMason/group-objects-by-property.md
Last active October 26, 2025 18:51
Show Gist options
  • Select an option

  • Save mikaello/06a76bca33e5d79cdd80c162d7774e9c to your computer and use it in GitHub Desktop.

Select an option

Save mikaello/06a76bca33e5d79cdd80c162d7774e9c to your computer and use it in GitHub Desktop.
Group Array of JavaScript Objects by Key or Property Value

Group array of JavaScript objects by keys

This fork of JamieMason's implementation changes the key parameter to be an array of keys instead of just a single key. This makes it possible to group by multiple properties instead of just one.

Implementation

const groupBy = keys => array =>
  array.reduce((objectsByKeyValue, obj) => {
    const value = keys.map(key => obj[key]).join('-');
    objectsByKeyValue[value] = (objectsByKeyValue[value] || []).concat(obj);
    return objectsByKeyValue;
  }, {});
Click to see TypeScript version
/**
 * Group array of objects by given keys
 * @param keys keys to be grouped by
 * @param array objects to be grouped
 * @returns an object with objects in `array` grouped by `keys`
 * @see <https://gist.github.com/mikaello/06a76bca33e5d79cdd80c162d7774e9c>
 */
const groupBy = <T>(keys: (keyof T)[]) => (array: T[]): Record<string, T[]> =>
  array.reduce((objectsByKeyValue, obj) => {
    const value = keys.map((key) => obj[key]).join('-');
    objectsByKeyValue[value] = (objectsByKeyValue[value] || []).concat(obj);
    return objectsByKeyValue;
  }, {} as Record<string, T[]>);

Usage

const cars = [
  { brand: 'Audi', produced: '2016', color: 'black' },
  { brand: 'Audi', produced: '2017', color: 'white' },
  { brand: 'Ford', produced: '2016', color: 'red' },
  { brand: 'Ford', produced: '2016', color: 'white' },
  { brand: 'Peugot', produced: '2018', color: 'white' }
];

const groupByBrand = groupBy(['brand']);
const groupByColor = groupBy(['color']);
const groupByBrandAndYear = groupBy(['brand', 'produced']);

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

Output

{
  "carsByBrand": {
    "Audi": [
      {
        "brand": "Audi",
        "produced": "2016",
        "color": "black"
      },
      {
        "brand": "Audi",
        "produced": "2017",
        "color": "white"
      }
    ],
    "Ford": [
      {
        "brand": "Ford",
        "produced": "2016",
        "color": "red"
      },
      {
        "brand": "Ford",
        "produced": "2016",
        "color": "white"
      }
    ],
    "Peugot": [
      {
        "brand": "Peugot",
        "produced": "2018",
        "color": "white"
      }
    ]
  },
  "carsByColor": {
    "black": [
      {
        "brand": "Audi",
        "produced": "2016",
        "color": "black"
      }
    ],
    "white": [
      {
        "brand": "Audi",
        "produced": "2017",
        "color": "white"
      },
      {
        "brand": "Ford",
        "produced": "2016",
        "color": "white"
      },
      {
        "brand": "Peugot",
        "produced": "2018",
        "color": "white"
      }
    ],
    "red": [
      {
        "brand": "Ford",
        "produced": "2016",
        "color": "red"
      }
    ]
  },
  "carsByBrandAndYear": {
    "Audi-2016": [
      {
        "brand": "Audi",
        "produced": "2016",
        "color": "black"
      }
    ],
    "Audi-2017": [
      {
        "brand": "Audi",
        "produced": "2017",
        "color": "white"
      }
    ],
    "Ford-2016": [
      {
        "brand": "Ford",
        "produced": "2016",
        "color": "red"
      },
      {
        "brand": "Ford",
        "produced": "2016",
        "color": "white"
      }
    ],
    "Peugot-2018": [
      {
        "brand": "Peugot",
        "produced": "2018",
        "color": "white"
      }
    ]
  }
}

See playcode.io for example.

@MaheshStark

Copy link
Copy Markdown

Thank you

@MaccerC

MaccerC commented Jul 29, 2020

Copy link
Copy Markdown

@mikaello In my code I use groupArray=require('group-array) which allows me to group an array on multiple properties. Your code does the same, but with a slightly different output.
You're concatenate the 2 keys like so: "Audi - 2016". What I want as outcome is: { "Audi": { 2016: [{...}] } }
Is this format also possible with your code?

@mikaello

Copy link
Copy Markdown
Author

@MaccerC

Is this format also possible with your code?

With a modification of how the key is created it is possible.

Group by with a nested structure:

const groupBy = (keys) => (array) =>
  array.reduce((objectsByKeyValue, obj) => {
   // Instead of creating a unique key for each grouped by values, we are now traversing (and building) 
   // the whole object structure for every array value:
    keys.reduce((builder, key, index) => {
      if (index !== keys.length - 1) {
        // Building the nested grouped by structure
        builder[obj[key]] = builder[obj[key]] || {};
      } else {
        // Appending the current object at the leaf node
        builder[obj[key]] = (builder[obj[key]] || []).concat(obj);
      }
      return builder[obj[key]];
    }, objectsByKeyValue);

    return objectsByKeyValue;
  }, {});

Example:

console.log(
  JSON.stringify(
    groupBy(["a", "b", "c"])([
      { a: "xxx", b: "yyy", c: "zzz", q: "111" },
      { a: "xxx", b: "yyy", c: "diff" },
      { a: "xxx", b: "yyy", c: "zzz", t: "222" },
    ]),
    null,
    2,
  ),
);

/*
{
  "xxx": {
    "yyy": {
      "zzz": [
        {
          "a": "xxx",
          "b": "yyy",
          "c": "zzz",
          "q": "111"
        },
        {
          "a": "xxx",
          "b": "yyy",
          "c": "zzz",
          "t": "222"
        }
      ],
      "diff": [
        {
          "a": "xxx",
          "b": "yyy",
          "c": "diff"
        }
      ]
    }
  }
}
*/

@MaccerC

MaccerC commented Aug 5, 2020

Copy link
Copy Markdown

@mikaello Very good! This code works like a charm. Thank you!

@yohDone

yohDone commented Sep 3, 2020

Copy link
Copy Markdown

Very efficient ! Just a question it’s possible to change the key name ? Exemples mix it with a String, convert it into number (for exemple if the value is a time stamp).

@mikaello

mikaello commented Sep 4, 2020

Copy link
Copy Markdown
Author

Just a question it’s possible to change the key name ? Exemples mix it with a String, convert it into number (for exemple is the value is a time stamp).

Sure, just change how the value variable is generated:

const groupBy = keys => array =>
  array.reduce((objectsByKeyValue, obj) => {
+   const value = someCustomGroupByKeyGenerator(keys.map(key => obj[key])); // Add your custom generator for keys here
    objectsByKeyValue[value] = (objectsByKeyValue[value] || []).concat(obj);
    return objectsByKeyValue;
  }, {});

The default is to just concatenate with -.

@yohDone

yohDone commented Sep 8, 2020

Copy link
Copy Markdown

Just a question it’s possible to change the key name ? Exemples mix it with a String, convert it into number (for exemple is the value is a time stamp).

Sure, just change how the value variable is generated:

const groupBy = keys => array =>
  array.reduce((objectsByKeyValue, obj) => {
+   const value = someCustomGroupByKeyGenerator(keys.map(key => obj[key])); // Add your custom generator for keys here
    objectsByKeyValue[value] = (objectsByKeyValue[value] || []).concat(obj);
    return objectsByKeyValue;
  }, {});

The default is to just concatenate with -.

Yay !! Thank you very much ! I will make something with it and come back with the result.

@shravanivish

Copy link
Copy Markdown

hi, can you please help

const arr = [
	{ Id: "001", qty: 1 },
	{ Id: "002", qty: 2 },
	{ Id: "001", qty: 2 },
	{ Id: "003", qty: 4 },
];

const result = [];
arr.reduce(function (res, value) {
	if (!res[value.Id]) {
		res[value.Id] = { Id: value.Id, qty: 0 };
		result.push(res[value.Id]);
	}
	res[value.Id].qty += value.qty;
	return res;
}, {});
console.log(result);
// Output:
// [ 
//   { Id: '001', qty: 3 }, 
//   { Id: '002', qty: 2 }, 
//   { Id: '003', qty: 4 } 
// ]

but, I need output as

{
"001":3,
"002":2,
"003":4
}

@mikaello

mikaello commented Aug 11, 2021

Copy link
Copy Markdown
Author

@shravanivish

hi, can you please help

This does not look related to the group by function in this gist, please write unrelated questions in other places that are more appropriate, e.g. Stackoverflow.

BTW, your problem could be fixed by using another reduce, or by modifying your existing. But please follow this another place.

@shravanivish

Copy link
Copy Markdown

@shravanivish

hi, can you please help

This does not look related to the group by function in this gist, please write unrelated questions in other places that are more appropriate, e.g. Stackoverflow.

BTW, your potsticker could be fixed by using another reduce, or by modifying your existing. But please follow this another place.

Thank You!

@melissapalmer

melissapalmer commented Sep 21, 2021

Copy link
Copy Markdown

@mikaello thanks for this, its really helpful!! Would it be possible to group nested values within an array?

For example if we had

const cars = [
  { brand: 'Audi', produced: '2016', color: 'black', drivers: [{ email: "Sebastian.Vettel@cars.com", firstName: "Sebastian", lastName: "Vettel" }, { email: "Lewis.Hamilton@cars.com", firstName: "Lewis", lastName: "Hamilton" }] },
  { brand: 'Audi', produced: '2017', color: 'white', drivers: [{ email: "Lewis.Hamilton@cars.com", firstName: "Lewis", lastName: "Hamilton" }] },
  { brand: 'Ford', produced: '2016', color: 'red', drivers: [{ email: "Lewis.Hamilton@cars.com", firstName: "Lewis", lastName: "Hamilton" }] },
  { brand: 'Ford', produced: '2016', color: 'white', drivers: [{ email: "Fernando.Alonso@cars.com", firstName: "Fernando", lastName: "Alonso" }] },
  { brand: 'Peugot', produced: '2018', color: 'white', drivers: [{ email: "Fernando.Alonso@cars.com", firstName: "Fernando", lastName: "Alonso" }, { email: "Lewis.Hamilton@cars.com", firstName: "Lewis", lastName: "Hamilton" }] }
];

Can we group by drivers.email? So result would that Lewis.Hamilton is associated with 4 cars?
eg:

{
  "Sebastian.Vettel@cars.com":
    [
      {"brand":"Audi","produced":"2016","color":"black","drivers": {"email":"Sebastian.Vettel@cars.com","firstName":"Sebastian","lastName":"Vettel"},{"email":"Lewis.Hamilton@cars.com","firstName":"Lewis","lastName":"Hamilton"}]}
    ],
  "Lewis.Hamilton@cars.com":
    [
      {"brand":"Audi","produced":"2016","color":"black","drivers":[{"email":"Sebastian.Vettel@cars.com","firstName":"Sebastian","lastName":"Vettel"},{"email":"Lewis.Hamilton@cars.com","firstName":"Lewis","lastName":"Hamilton"}]},
      {"brand":"Audi","produced":"2017","color":"white","drivers":[{"email":"Lewis.Hamilton@cars.com","firstName":"Lewis","lastName":"Hamilton"}]},
      {"brand":"Ford","produced":"2016","color":"red","drivers":[{"email":"Lewis.Hamilton@cars.com","firstName":"Lewis","lastName":"Hamilton"}]},             
      {"brand":"Peugot","produced":"2018","color":"white","drivers":[{"email":"Fernando.Alonso@cars.com","firstName":"Fernando","lastName":"Alonso"},{"email":"Lewis.Hamilton@cars.com","firstName":"Lewis","lastName":"Hamilton"}]}
    ],
  "Fernando.Alonso@cars.com":
    [
      {"brand":"Ford","produced":"2016","color":"white","drivers":[{"email":"Fernando.Alonso@cars.com","firstName":"Fernando","lastName":"Alonso"}]},      {"brand":"Peugot","produced":"2018","color":"white","drivers":[{"email":"Fernando.Alonso@cars.com","firstName":"Fernando","lastName":"Alonso"},{"email":"Lewis.Hamilton@cars.com","firstName":"Lewis","lastName":"Hamilton"}]}
    ]
}

@maciel-82

maciel-82 commented Oct 5, 2021

Copy link
Copy Markdown

is there any way to get the count which group have

Yes, that is possible. The values of a group is just an array, so you could just check the length property of that array:

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

const cars = [
  { brand: "Audi", produced: "2016", color: "black" },
  { brand: "Audi", produced: "2017", color: "white" },
  { brand: "Ford", produced: "2016", color: "red" },
  { brand: "Ford", produced: "2016", color: "white" },
  { brand: "Peugot", produced: "2018", color: "white" },
];

const groupByBrandAndYear = groupBy(["brand", "produced"]);

for (let [groupName, values] of Object.entries(groupByBrandAndYear(cars))) {
  console.log(`${groupName}: ${values.length}`);
}
# console output
Audi-2016: 1
Audi-2017: 1
Ford-2016: 2
Peugot-2018: 1

@mikaello nice work!
BTW is it possible to keep the brand and produced attributes separated in the result, instead of concatenating them?

like this:
{brand: "Audi", produced: 2016, count: 1}
{brand: "Ford", produced: 2016, count: 2}
... and so on.

Thanks

@mikaello

mikaello commented Oct 6, 2021

Copy link
Copy Markdown
Author

@maciel-82 , sure that is possible. But it may be easier to just modify the result accordingly, e.g.

const brandYearCount = Object
  .entries(groupByBrandAndYear(cars))
  .map(([, value]) =>
    ({
      brand: value[0].brand,
      produced: value[0].produced,
      count: value.length
    }))

console.log(brandYearCount)
# Output
[
 {  brand: "Audi",  count: 1,  produced: "2016"},
 {  brand: "Audi",  count: 1,  produced: "2017"},
 {  brand: "Ford",  count: 2,  produced: "2016"},
 {  brand: "Peugot",  count: 1,  produced: "2018"}
]

See JsFiddle

@maciel-82

Copy link
Copy Markdown

@maciel-82 , sure that is possible. But it may be easier to just modify the result accordingly, e.g.

const brandYearCount = Object
  .entries(groupByBrandAndYear(cars))
  .map(([, value]) =>
    ({
      brand: value[0].brand,
      produced: value[0].produced,
      count: value.length
    }))

console.log(brandYearCount)
# Output
[
 {  brand: "Audi",  count: 1,  produced: "2016"},
 {  brand: "Audi",  count: 1,  produced: "2017"},
 {  brand: "Ford",  count: 2,  produced: "2016"},
 {  brand: "Peugot",  count: 1,  produced: "2018"}
]

See JsFiddle

@mikaello it worked perfectly!!!
Thanks a lot!

@Taspee

Taspee commented Nov 15, 2022

Copy link
Copy Markdown

@maciel-82 , sure that is possible. But it may be easier to just modify the result accordingly, e.g.

const brandYearCount = Object
  .entries(groupByBrandAndYear(cars))
  .map(([, value]) =>
    ({
      brand: value[0].brand,
      produced: value[0].produced,
      count: value.length
    }))

console.log(brandYearCount)
# Output
[
 {  brand: "Audi",  count: 1,  produced: "2016"},
 {  brand: "Audi",  count: 1,  produced: "2017"},
 {  brand: "Ford",  count: 2,  produced: "2016"},
 {  brand: "Peugot",  count: 1,  produced: "2018"}
]

See JsFiddle

@mikaello, In this case, how could i get the brand that was produce the most per year?

@aacassandra

Copy link
Copy Markdown

thanks, its great

@reddo

reddo commented Sep 5, 2023

Copy link
Copy Markdown
const groupBy = (keys) => (array) =>
  array.reduce((objectsByKeyValue, obj) => {
   // Instead of creating a unique key for each grouped by values, we are now traversing (and building) 
   // the whole object structure for every array value:
    keys.reduce((builder, key, index) => {
      if (index !== keys.length - 1) {
        // Building the nested grouped by structure
        builder[obj[key]] = builder[obj[key]] || {};
      } else {
        // Appending the current object at the leaf node
        builder[obj[key]] = (builder[obj[key]] || []).concat(obj);
      }
      return builder[obj[key]];
    }, objectsByKeyValue);

    return objectsByKeyValue;
  }, {});

I know this is an old gist, but Is there any way you could help me with the typescript version of this?

@kkg0

kkg0 commented Dec 9, 2023

Copy link
Copy Markdown

how can i iterate through returned array ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment