fede_rosario
fede_rosario

Reputation: 105

Flatten array into one object while reducing one property

I have an object which contains arrays of worked dates of two employees, gruped by their id.

var employeeWorkedHours = {
  1: [
     { employeeId: 1, hours: 1, date: 7/1/2017},
     { employeeId: 1, hours: 2, date: 7/2/2017},
     { employeeId: 1, hours: 3, date: 7/3/2017},
     { employeeId: 1, hours: 4, date: 7/4/2017},
     { employeeId: 1, hours: 5, date: 7/5/2017}
],
2: [
   { employeeId: 2, hours: 5, date: 7/1/2017},
   { employeeId: 2, hours: 4, date: 7/2/2017},
   { employeeId: 2, hours: 3, date: 7/3/2017},
   { employeeId: 2, hours: 2, date: 7/4/2017},
   { employeeId: 2, hours: 1, date: 7/5/2017}
] }

I want to iterate over it, and merge each array into a single object:

Object.keys(employeeWorkedHours).map((emp) => { 
  emp.map((day) => {
    // expected output: one single object
    // { employeeId: X, totalHours: sum of hours of each line }
  })}
})}

Thanks

Upvotes: 1

Views: 78

Answers (1)

trincot
trincot

Reputation: 350137

You can use reduce for that:

const employeeWorkedHours = {
  1: [
     { employeeId: 1, hours: 1, date: 7/1/2017},
     { employeeId: 1, hours: 2, date: 7/2/2017},
     { employeeId: 1, hours: 3, date: 7/3/2017},
     { employeeId: 1, hours: 4, date: 7/4/2017},
     { employeeId: 1, hours: 5, date: 7/5/2017}
],
2: [
   { employeeId: 2, hours: 5, date: 7/1/2017},
   { employeeId: 2, hours: 4, date: 7/2/2017},
   { employeeId: 2, hours: 3, date: 7/3/2017},
   { employeeId: 2, hours: 2, date: 7/4/2017},
   { employeeId: 2, hours: 1, date: 7/5/2017}
] };

const result = Object.keys(employeeWorkedHours).map((emp) => { 
  return employeeWorkedHours[emp].reduce((acc, day) => {
    acc.totalHours += day.hours;
    return acc;
  }, {employeeId: +emp, totalHours: 0} );  
});

console.log(result);

Explanation

Object.keys(employeeWorkedHours) will give the array of property names of the outer object, so ["1", "2"]. In the map callback, emp will get each of these two values. So employeeWorkedHours[emp] is the array associated with that key.

Then reduce will build and return the desired object for that employee as follows: it starts with value {employeeId: +emp, totalHours: 0} (the second argument to reduce), which becomes the first argument of the callback, i.e. acc. The second argument of the callback takes the value of each inner object for that employee (e.g. { employeeId: 1, hours: 1, date: 7/1/2017}). The hours of that entry are added to the object that is being accumulated (to acc). The mutated acc object is returned again to the reduce internals, which will pass it to the next call of the callback. And so the object is finalised and eventually returned by reduce.

The result of reduce is returned to the map internals, which in turn will return an array of results: one value (object) per employee.

Assumption

I assume the properties of the main object correspond to the employeeId of the inner objects. If this is not the case, then you would need to add:

acc.employeeId = day.employeeId;

Upvotes: 2

Related Questions