Reputation: 2709
I built the following method using reduce to reduce data from DB to a better shape for my needs and would like to know how to simplify it and make it better
const dbData = [{
studyId: "X",
siteId: "A",
day: "2000-01-01",
status: "PENDING_CALLCENTER",
current: 5,
total: 17
},
{
studyId: "X",
siteId: "A",
day: "2000-01-01",
status: "PENDING_SITE",
current: 3,
total: 9
},
{
studyId: "Y",
siteId: "B",
day: "2000-01-01",
status: "PENDING_SITE",
current: 3,
total: 9
},
{
studyId: "Y",
siteId: "B",
day: "2000-01-01",
status: "PENDING_CALLCENTER",
current: 3,
total: 9
}
];
const reduced = dbData.reduce((acc, row) => {
const {
studyId,
siteId,
status,
current,
total
} = row;
const idx = acc.findIndex(
(x) => studyId === x.studyId && siteId === x.siteId
);
const item =
idx === -1 ?
{
studyId,
siteId,
currents: {},
totals: {}
} :
{ ...acc[idx]
};
item.currents[status] = item.currents[status] ?
item.currents[status] + current :
current;
item.totals[status] = item.totals[status] ?
item.totals[status] + total :
total;
if (idx === -1) {
acc.push(item);
} else {
acc[idx] = item;
}
return acc;
}, []);
console.log(reduced);
The method gets data like from the example an array of objects where we have as a duplicate the same studyId
and siteId
and that data contains always a different status
.
The method needs to aggregate this data in one object per the same studyId
and siteId
like the result of the snippet including all the statuses divided by currents
and totals.
As a simple example, we will never have the same data for status for that study and site plus the current and total are related to that status only
{
studyId: A
siteId: 1
status: PENDING_CALLCENTER
current: 1
total: 1
}
{
studyId: A
siteId: 1
status: PENDING_SITE
current: 3
total: 5
}
{
studyId: A
siteId: 1
status: ANOTHER_STATUS
current: 34
total: 50
}
The above will be resulting as follow
{
studyId: A
siteId: 1
currents: [{
PENDING_CALLCENTER: 1
PENDING_SITE: 3
ANOTHER_STATUS: 34
}]
totals: [{
PENDING_CALLCENTER: 1
PENDING_SITE: 5
ANOTHER_STATUS: 50
}]
}
In this way, that data is altogether making it easier to use in further steps.
What I need to understand is how can I make this better as I do not really like the way I found it with reduce, for example, that reduce can be improved or can be used lodash to get same result or another method approach.
Upvotes: 0
Views: 105
Reputation: 370699
Instead of using findIndex
with the accumulator being an array, you can have an object instead, indexed by a unique key for a given combination of a studyId and siteId.
Instead of the conditional operator to check if the nested value exists when trying to add, you can alternate the existing value ?? 0
so that it defaults to 0 if the value isn't found, which makes the syntax a bit easier.
const dbData=[{studyId:"X",siteId:"A",day:"2000-01-01",status:"PENDING_CALLCENTER",current:5,total:17},{studyId:"X",siteId:"A",day:"2000-01-01",status:"PENDING_SITE",current:3,total:9},{studyId:"Y",siteId:"B",day:"2000-01-01",status:"PENDING_SITE",current:3,total:9},{studyId:"Y",siteId:"B",day:"2000-01-01",status:"PENDING_CALLCENTER",current:3,total:9}];
const dataByKey = {};
for (const { studyId, siteId, status, current, total } of dbData) {
const key = `${studyId}_${siteId}`;
const thisData = dataByKey[key] ??= {
studyId,
siteId,
currents: {},
totals: {}
};
thisData.currents[status] = (thisData.currents[status] ?? 0) + current;
thisData.totals[status] = (thisData.totals[status] ?? 0) + total;
}
console.log(Object.values(dataByKey));
A semi-common pattern I see some people use, that I think is a slight mistake, is to use .reduce
when transforming an array into an object (or into an array that isn't one-to-one or a filter of the original array). IMO, using a plain loop is both easier to read and easier to write in most circumstances. See: Is reduce bad?
Upvotes: 2