I have a data structure of an array of objects with number of hours spent for a number of different roles like below:
[{"week 01" : {"Developer" : 1}},
{"week 01" : {"Project Manager" : 5}},
{"week 01" : {"Project Manager" : 6}},
{"week 01": {"Developer" : 8}},
{"week 02" : {"Strategy" : 4}}]
The roles and weeks are dynamic (i.e. I don't know the key/values beforehand) and I would like to sum all the hours for the same role and same week to get the below output:
[{"week 01" : {
"Developer" : 9
"Project Manager" : 11
}
},
{"week 02" : {
"Strategy" : 4
}
}]
I have tried using the reduce function, but I can't seem to get it right:
function reduceArray(arr) {
const result = arr.reduce((result, item) => {
let obj = {};
const existing = result.find(function (x) {
for (var prop in x) {
if (x[prop] === item[prop]) {
//same week number found
//loop through roles
for (var subProp in x[prop]) {
if (x[prop][subProp] === item[prop][subProp]) {
obj[prop][subProp] += x[prop][subProp] + item[prop][subProp]
//This is where I lose myself in the prop-loops....
}
}
}
}
})
}, [])
return result;
}
const data = [
{ "week 01": { Developer: 1 } },
{ "week 01": { "Project Manager": 5 } },
{ "week 01": { "Project Manager": 6 } },
{ "week 01": { Developer: 8 } },
{ "week 02": { Strategy: 4 } },
];
const result = data.reduce(
(prev, item) => {
for (const week in item) {
if (prev[week]) {
for (const act in item[week]) {
if (prev[week][act]) {
prev[week][act] += item[week][act];
} else {
prev[week][act] = item[week][act];
}
}
} else {
prev[week] = item[week];
}
}
return prev;
},
{}
);
console.log(result);
You could generate a single object with the accumulated values for all types.
const
data = [{ "week 01": { Developer: 1 } }, { "week 01": { "Project Manager": 5 } }, { "week 01": { "Project Manager" : 6 } }, { "week 01": { Developer : 8 } }, { "week 02": { Strategy: 4 } }],
result = data.reduce((r, o) => {
Object
.entries(o)
.forEach(([week, q]) => Object
.entries(q)
.forEach(([type, value]) => {
(r[week] ??= {})[type] ??= 0;
r[week][type] += value;
})
);
return r;
}, {});
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Here's a simple way to do it. Hopefully the comments are useful.
const data = [
{ "week 01": { Developer: 1 } },
{ "week 01": { "Project Manager": 5 } },
{ "week 01": { "Project Manager": 6 } },
{ "week 01": { Developer: 8 } },
{ "week 02": { Strategy: 4 } }
];
function cleanUp(data) {
return data.reduce((acc, item) => {
// assign variables
const week = Object.keys(item).pop();
const [job, value] = Object.entries(item[week]).pop();
// initialise them, if they aren't in the accumilator.
if (!acc[week]) acc[week] = {};
if (!acc[week][job]) acc[week][job] = 0;
// add the value
acc[week][job] += value;
return acc;
}, {});
}
console.log(cleanUp(data));
Here's one that uses for...in instead of Object.keys() and Object.entries(),
const data = [
{ "week 01": { Developer: 1 } },
{ "week 01": { "Project Manager": 5 } },
{ "week 01": { "Project Manager": 6 } },
{ "week 01": { Developer: 8 } },
{ "week 02": { Strategy: 4 } }
];
function cleanUp(data) {
return data.reduce((acc, item) => {
for (const week in item) {
if (!acc[week]) acc[week] = {};
for (const job in item[week]) {
if (!acc[week][job]) acc[week][job] = 0;
acc[week][job] += item[week][job];
}
}
return acc;
}, {});
}
console.log(cleanUp(data));