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