I need to merge the objects together. The resource property is what determines if the objects can be merged. To determine where the hours property valu
One technique is to loop over each member, find the first one with that resource, update the totals in it, and then filter so as to retain only the first occurrences.
members.filter(member => {
const first = members.find(m => m.resource === member.resource);
if (member.billable) first.totalBillableHours += member.hours;
else first.totalNonBillableHours += member.hours;
first.totalHours += member.hours.
return first === member;
});
A more orthodox approach would be to group the objects by resource, creating an array of objects for each resource, and then transform that into your desired output, as in
totals(groupBy(members, 'resource'))
groupBy would be defined as producing something of the form:
{
resource1: [obj, obj],
resource2: [obj, obj]
}
To take totals first, that would be
function totals(groups) {
const hours = m => m.hours;
const billable = m => m.billable;
const not = f => x => !f(x);
return Object.keys(groups).map(resource => {
const members = groups[resource];
const totalHours = sum(members.map(hours));
const billableHours = sum(members.filter(billable).map(hours));
const nonBillableHours = sum(members.filter(not(billable)).map(hours));
return {resource, totalHours, billableHours, nonBillableHours};
});
}
sum can be written as
const sum = arr => arr.reduce((a, b) => a + b, 0);
There are many implementations of groupBy out there, including ones provided by libraries such as underscore. Here's a real simple version:
function groupBy(arr, prop) {
return arr.reduce((result, obj) {
const key = obj[prop];
if (!result[key]) result[key] = [];
result[key].push(obj);
return result;
}, {});
}