-1

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

fedjedmedjed
  • 415
  • 1
  • 6
  • 16
  • 2
    What is `groupBy()`? It's not standard JS. Did you mean [Array.prototype.group()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/group)? – Phil Feb 05 '23 at 23:19
  • @Phil yes you are right, it is part of proposals from core-js library. I believe Array.prototype.group() would yield the same result. – fedjedmedjed Feb 05 '23 at 23:25
  • Does this answer your question? [Javascript - Array of objects group by year, month and date](https://stackoverflow.com/questions/43701245/javascript-array-of-objects-group-by-year-month-and-date) – pilchard Feb 05 '23 at 23:45
  • also [Group Array of Objects to Year and Month by Date](https://stackoverflow.com/questions/71741361/group-array-of-objects-to-year-and-month-by-date) – pilchard Feb 05 '23 at 23:47
  • also: [Group Array of Object by multiple keys](https://stackoverflow.com/questions/72875952/group-array-of-object-by-multiple-keys/72876210#72876210) – pilchard Feb 06 '23 at 10:35

1 Answers1

4

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

Peter Thoeny
  • 7,379
  • 1
  • 10
  • 20