Home > Mobile >  Group array of objects by two properties
Group array of objects by two properties

Time:02-06

I'm trying to group array of objects by two properties so I get data on two levels. This is in JavaScript - Node server. Here is the data I'm starting with

items = [
  {month: 11, year: 2022, amount: 10},
  {month: 12, year: 2022, amount: 6},
  {month: 11, year: 2022, amount: 7},
  {month: 12, year: 2022, amount: 12},
  {month: 1, year: 2023, amount: 5},
  {month: 1, year: 2023, amount: 15},
  {month: 11, year: 2023, amount: 30},
  {month: 11, year: 2023, amount: 20}
],

I need all items from the same month and in the same year grouped together. The final result would look like this:

result = {
  "2022": {
    "11": [
      {month: 11, year: 2022, amount: 10},
      {month: 11, year: 2022, amount: 7}
    ],
    "12": [
      {month: 12, year: 2022, amount: 6},
      {month: 12, year: 2022, amount: 12}
    ]
  },
  "2023": {
    "11": [
      {month: 11, year: 2023, amount: 30},
      {month: 11, year: 2023, amount: 20}
    ],
    "1": [
      {month: 1, year: 2023, amount: 5},
      {month: 1, year: 2023, amount: 15}
    ]
  }
}

The first step is done quite easily, either through reduce or groupBy(), I've opted for groupBy() because it is cleaner:

const itemsPerYear = items.groupBy(item => { return item.year })

This gives me intermediate result:

itemsPerYear = {
  "2022": [
      {month: 11, year: 2022, amount: 10},
      {month: 11, year: 2022, amount: 7},
      {month: 12, year: 2022, amount: 6},
      {month: 12, year: 2022, amount: 12}
    ],
  "2023": [
      {month: 11, year: 2023, amount: 30},
      {month: 11, year: 2023, amount: 20},
      {month: 1, year: 2023, amount: 5},
      {month: 1, year: 2023, amount: 15}
    ]
}

So if I apply similar logic and go with:

const itemsPerMonth = Object.values(itemsPerYear).groupBy(item => { return item.month })

I get:

[Object: null prototype] {
  undefined: [
    [ [Object], [Object], [Object], [Object] ],
    [ [Object], [Object], [Object], [Object] ]
  ]
}

I get a step closer with ForEach: const itemsPerMonth = Object.values(itemsPerYear).forEach(sub => { console.log(sub) }) I get:

[
    {month: 11, year: 2022, amount: 10},
    {month: 11, year: 2022, amount: 7},
    {month: 12, year: 2022, amount: 6},
    {month: 12, year: 2022, amount: 12}
],
[
    {month: 11, year: 2023, amount: 30},
    {month: 11, year: 2023, amount: 20},
    {month: 1, year: 2023, amount: 5},
    {month: 1, year: 2023, amount: 15}
]

If I want to use groupBy() inside the ForEach() I get undefined.

Thanks

CodePudding user response:

Here is a quick solution using a .reduce():

const input = [ {month: 11, year: 2022, amount: 10}, {month: 12, year: 2022, amount: 6}, {month: 11, year: 2022, amount: 7}, {month: 12, year: 2022, amount: 12}, {month: 1, year: 2023, amount: 5}, {month: 1, year: 2023, amount: 15}, {month: 11, year: 2023, amount: 30}, {month: 11, year: 2023, amount: 20} ];

const result = input.reduce((acc, obj) => {
  if(!acc[obj.year]) acc[obj.year] = {};
  if(!acc[obj.year][obj.month]) acc[obj.year][obj.month] = [];
  acc[obj.year][obj.month].push(obj);
  return acc;
}, {});
console.log('result:', result);

Docs: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce

  • Related