We can do it like this (nested reduce):
const input = {
groupA: [
{name: "Adam", age: 25, Job: "Pharmacist", group: 'groupA'},
{name: "Eric", age: 25, Job: "Vet", group: 'groupA'},
{name: "Bob", age: 26, Job: "Pharmacist", group: 'groupA'},
{name: "Peter", age: 26, Job: "Vet", group: 'groupA'},
],
groupB: [
{name: "Adam", age: 25, Job: "Pharmacist", group: 'groupB'},
{name: "Eric", age: 25, Job: "Vet", group: 'groupB'},
{name: "Bob", age: 26, Job: "Pharmacist", group: 'groupB'},
{name: "Peter", age: 26, Job: "Vet", group: 'groupB'},
]
}
const output = (property) => {
return Object.entries(input).reduce((aggObj, [key,val]) => {
const grouped = val.reduce((groupedObj, item) => {
if (groupedObj.hasOwnProperty(item[property])){
groupedObj[item[property]].push(item);
} else{
groupedObj[item[property]] = [item];
}
return groupedObj;
}, {});
aggObj[key] = grouped;
return aggObj;
}, {})
}
console.log(output('Job'));
//we could also do the same for age:
//console.log(output('age'));
/* Desired output: */
/*
const output = {
groupA: {
pharmacist: [
{name: "Adam", age: 25, Job: "Pharmacist", group: 'groupA'},
{name: "Bob", age: 25, Job: "Pharmacist", group: 'groupA'},
],
vet: [
{name: "Eric", age: 25, Job: "Vet", group: 'groupA'},
{name: "Peter", age: 25, Job: "Vet", group: 'groupA'},
]
},
groupB: {
pharmacist: [
{name: "Adam", age: 25, Job: "Pharmacist", group: 'groupB'},
{name: "Bob", age: 25, Job: "Pharmacist", group: 'groupB'},
],
vet: [
{name: "Eric", age: 25, Job: "Vet", group: 'groupB'},
{name: "Peter", age: 25, Job: "Vet", group: 'groupB'},
]
},
}
*/
In essence, make an iterable from the Object with Object.entries(input)
Then iterate over these ([key, val]) entries and extract the key (i.e. first "GroupA" then "GroupB")
Each one of these values (val) is an array so we can reduce on it (i.e. val.reduce())
And then push each array that matches item.Job to the grouped object.
Then assign this to the final returned aggregated object and the end.
Update - recursive solution to groupBy up to N levels deep nested recursion
I wanted to make it really generic and make an assumption about how your initial data looked and allow you do nested grouping like this on other properties (as nested as you want). That capability is displayed lower down in the answer (i.e. 3 levels deep grouping) but for now, here is your specific example:
You can call it simply like this groupBy(input, ["group", "Job"])
Where the result is grouped by group, then by Job, (nested).
for your specific example:
//assumed initial input, array of objects:
const input = [
{ name: "Adam", age: 25, Job: "Pharmacist", group: "groupA" },
{ name: "Eric", age: 25, Job: "Vet", group: "groupA" },
{ name: "Bob", age: 26, Job: "Pharmacist", group: "groupA" },
{ name: "Peter", age: 26, Job: "Vet", group: "groupA" },
{ name: "Adam", age: 25, Job: "Pharmacist", group: "groupB"},
{ name: "Eric", age: 25, Job: "Vet", group: "groupB" },
{ name: "Bob", age: 26, Job: "Pharmacist", group: "groupB" },
{ name: "Peter", age: 26, Job: "Vet", group: "groupB" }
];
const groupBy = (input, propertyArr) => {
//console.log(propertyArr);
const property = propertyArr[0];
const grouped = input.reduce((groupedObj, item) => {
groupedObj[item[property]] = [...(groupedObj[item[property]] || []), item];
return groupedObj;
}, {});
if (propertyArr.length > 1) {
//console.log(grouped);
return Object.keys(grouped).reduce((AggObj, key, index) => {
const propertyArrCopy = [...propertyArr];
propertyArrCopy.shift();
AggObj[key] = groupBy(grouped[key], propertyArrCopy);
return AggObj;
}, {});
} else {
return grouped;
}
};
console.log(groupBy(input, ["group", "Job"]));
.as-console-wrapper { max-height: 100% !important; top: 0; }
With groupBy(input, ["group", "Job"])We get the output you are expecting:
{
"groupA": {
"Pharmacist": [
{"name": "Adam", "age": 25, "Job": "Pharmacist", "group": "groupA"},
{"name": "Bob", "age": 26, "Job": "Pharmacist", "group": "groupA"}
],
"Vet": [
{"name": "Eric", "age": 25, "Job": "Vet", "group": "groupA"},
{"name": "Peter", "age": 26, "Job": "Vet", "group": "groupA"}
]
},
"groupB": {
"Pharmacist": [
{"name": "Adam", "age": 25, "Job": "Pharmacist", "group": "groupB"},
{"name": "Bob", "age": 26, "Job": "Pharmacist", "group": "groupB"}
],
"Vet": [
{"name": "Eric", "age": 25, "Job": "Vet", "group": "groupB"},
{"name": "Peter", "age": 26, "Job": "Vet", "group": "groupB"}
]
}
}
Example of the capability of the more general solution with 3 levels deep grouping:
i.e. calling groupBy(input, ["group", "Job", "age"])
Where the result is grouped by group, then by Job, then by age (nested).
//assumed initial input, array of objects:
const input = [
{ name: "Adam", age: 25, Job: "Pharmacist", group: "groupA" },
{ name: "Lauren", age: 25, Job: "Pharmacist", group: "groupA" },
{ name: "Eric", age: 25, Job: "Vet", group: "groupA" },
{ name: "Theresa", age: 25, Job: "Vet", group: "groupA" },
{ name: "Bob", age: 26, Job: "Pharmacist", group: "groupA" },
{ name: "Brandy", age: 26, Job: "Pharmacist", group: "groupA" },
{ name: "Alex", age: 26, Job: "Scientist", group: "groupA" },
{ name: "Tom", age: 26, Job: "Scientist", group: "groupA" },
{ name: "Peter", age: 26, Job: "Vet", group: "groupA" },
{ name: "Kate", age: 26, Job: "Vet", group: "groupA" },
{ name: "Adam", age: 25, Job: "Pharmacist", group: "groupB" },
{ name: "Sarah", age: 25, Job: "Pharmacist", group: "groupB" },
{ name: "Eric", age: 25, Job: "Vet", group: "groupB" },
{ name: "Sophie", age: 25, Job: "Vet", group: "groupB" },
{ name: "Bob", age: 26, Job: "Pharmacist", group: "groupB" },
{ name: "Anne", age: 26, Job: "Pharmacist", group: "groupB" },
{ name: "Peter", age: 26, Job: "Vet", group: "groupB" },
{ name: "Mary", age: 26, Job: "Vet", group: "groupB" },
{ name: "Alex", age: 26, Job: "Scientist", group: "groupB" },
{ name: "Sarah", age: 26, Job: "Scientist", group: "groupB" }
];
const groupBy = (input, propertyArr) => {
//console.log(propertyArr);
const property = propertyArr[0];
const grouped = input.reduce((groupedObj, item) => {
groupedObj[item[property]] = [...(groupedObj[item[property]] || []), item];
return groupedObj;
}, {});
if (propertyArr.length > 1) {
//console.log(grouped);
return Object.keys(grouped).reduce((AggObj, key, index) => {
const propertyArrCopy = [...propertyArr];
propertyArrCopy.shift();
AggObj[key] = groupBy(grouped[key], propertyArrCopy);
return AggObj;
}, {});
} else {
return grouped;
}
};
console.log(groupBy(input, ["group", "Job", "age"]));
.as-console-wrapper { max-height: 100% !important; top: 0; }
I added some more objects into the initial array to make it a bit more interesting. This can also be extended for much larger data sets and with many more levels of nesting and should work fine.
groupBy(input, ["group", "Job"])it it will return your desired output. You can also call this as nested as you like such asgroupBy(input, ["group", "Job", "age"])- stackoverflow.com/a/60741156/9792594