Reputation: 729
I am working on a function that takes an array of objects and returns an object with the data grouped by values of a nested array property:
INPUT
[
{
id: 0,
section: ['valueX']
},
{
id: 1,
section: ['valueX']
},
{
id: 2,
section: ['valueY']
},
{
id: 3,
section: ['valueY', 'valueX', 'valueZ']
},
{
id: 4,
section: []
}
];
OUTPUT
{
valueX: [{...}]
valueY: [{...}]
valueZ: [{...}],
all: [{...}]
}
I have this working using reduce and a couple of nested forEach loops. I wanted to see what other alternatives are out there. I do not want to use any library or implement extremely unreadable code.
MY ATTEMPT
const groupBy = ({ data, property }) =>
data.reduce((arr, current) => {
const result = arr;
const groupList = current[property];
const groupHasItems = groupList.length > 0;
const groupItemExists = result[groupList];
if (groupHasItems && !groupItemExists) {
groupList.forEach(id => {
if (!result[id]) {
result[id] = [];
}
});
} else if (!result.all) {
result.all = [];
}
if (groupHasItems) {
groupList.forEach(id => {
result[id].push(current);
});
} else {
result.all.push(current);
}
return result;
}, {});
Upvotes: 1
Views: 296
Reputation: 4241
I have a recursive way which could handle deeper nesting etc., which doesn't seem like it's needed for you(?) but it's there if you want it. If you only want 1 level deep, then we can also simplify it.
const input = [
{id: 0, section: ['valueX']},
{id: 1, section: ['valueX']},
{id: 2, section: ['valueY']},
{id: 3, section: ['valueY', 'valueX', 'valueZ']},
{id: 4, section: []}
];
const groupBy = (input, propertyArr) => {
//console.log(propertyArr);
const property = propertyArr[0];
const grouped = input.reduce((groupedObj, item) => {
const key = item[property];
if (key instanceof Array && key.length > 0){
key.forEach(k => {
groupedObj[k] = [...(groupedObj[k] || []), item];
})
}else if (key instanceof Array){
groupedObj['all'] = [...(groupedObj['all'] || []), item];
}else{
groupedObj[key] = [...(groupedObj[key] || []), 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;
}
};
const grouped = groupBy(input, ["section"]);
console.log(grouped);
const inputNested = [
{id: 0, somekey: 'someValA', section: ['valueX']},
{id: 1, somekey: 'someValB', section: ['valueX']},
{id: 2, somekey: 'someValA', section: ['valueY']},
{id: 3, somekey: 'someValB', section: ['valueY', 'valueX', 'valueZ']},
{id: 4, somekey: 'someValA', section: []}
];
//Nested Example
//const nestedGrouped = groupBy(inputNested, ["somekey", "section"]);
//console.log(nestedGrouped);
.as-console-wrapper { max-height: 100% !important; top: 0; }
The simplified code for your specific 1-level deep problem:
const input = [
{id: 0, section: ['valueX']},
{id: 1, section: ['valueX']},
{id: 2, section: ['valueY']},
{id: 3, section: ['valueY', 'valueX', 'valueZ']},
{id: 4, section: []}
];
const groupBy = (input, propertyArr) => {
//console.log(propertyArr);
const property = propertyArr[0];
const grouped = input.reduce((groupedObj, item) => {
const key = item[property];
if (key instanceof Array && key.length > 0){
key.forEach(k => {
groupedObj[k] = [...(groupedObj[k] || []), item];
})
}else{
groupedObj['all'] = [...(groupedObj['all'] || []), item];
}
return groupedObj;
}, {});
return grouped;
};
const nestedGrouped = groupBy(input, ["section"]);
console.log(nestedGrouped);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Here is your code for reference:
const input = [
{id: 0, section: ['valueX']},
{id: 1, section: ['valueX']},
{id: 2, section: ['valueY']},
{id: 3, section: ['valueY', 'valueX', 'valueZ']},
{id: 4, section: []}
];
const groupBy = ({ data, property }) =>
data.reduce((arr, current) => {
const result = arr;
const groupList = current[property];
const groupHasItems = groupList.length > 0;
const groupItemExists = result[groupList];
if (groupHasItems && !groupItemExists) {
groupList.forEach(id => {
if (!result[id]) {
result[id] = [];
}
});
} else if (!result.all) {
result.all = [];
}
if (groupHasItems) {
groupList.forEach(id => {
result[id].push(current);
});
} else {
result.all.push(current);
}
return result;
}, {});
console.log(groupBy({ data: input, property: 'section'}))
.as-console-wrapper { max-height: 100% !important; top: 0; }
Upvotes: 1