skip to Main Content

I have an Array of objects, I want to group the data by Project Name and, in the case of an existing project, sum the hours.

projects = [
    {
      "project": { "id": 1, "name": "Mars Rover" },
      "employee": { "id": 1, "name": "Mario" },
      "date": "2021-08-26T22:00:00.000Z",
      "hours": 5
    },
    {
      "project": { "id": 2, "name": "Manhattan" },
      "employee": { "id": 2, "name": "Giovanni" },
      "date": "2021-08-30T22:00:00.000Z",
      "hours": 3
    },
    {
      "project": { "id": 1, "name": "Mars Rover" },
      "employee": { "id": 1, "name": "Mario" },
      "date": "2021-08-31T22:00:00.000Z",
      "hours": 3
    },
    {
      "project": { "id": 1, "name": "Mars Rover" },
      "employee": { "id": 3, "name": "Lucia" },
      "date": "2021-08-31T22:00:00.000Z",
      "hours": 3
    },
    {
      "project": { "id": 2, "name": "Manhattan" },
      "employee": { "id": 1, "name": "Mario" },
      "date": "2021-08-26T22:00:00.000Z",
      "hours": 2
    },
    {
      "project": { "id": 2, "name": "Manhattan" },
      "employee": { "id": 2, "name": "Giovanni" },
      "date": "2021-08-31T22:00:00.000Z",
      "hours": 4
    }
  ]
}

This is how I want the data to be after carrying out the sum

projects = [
{project: "Mars Rover", hours: 11},
{project: "Manhattan", hours: 9}
]

This is the code I wrote.

 let newRows = [];

 projects.forEach((element) => {
    if (newRows.length === 0) {
      newRows.push(element);
    } else {
      const index = newRows.findIndex(
        (project) => project.project.id === element.project.id
      );
      console.log(index);
      if (index > -1) {
        newRows[index].hours += element.hours;
      } else {
        newRows.push(element);
      }
    }
  });

I created a new array and went to populate it. I researched the common id.
This code works but I am getting a bug. In practice, every time I write code in that file, the total hours on the screen increase by themselves, then I reload the page and everything returns as before.

enter image description here

4

Answers


  1. Chosen as BEST ANSWER

    I have another case to handle, and I'd like to take the same approach. I always have the same array of objects:

      projects = [
        {
          "project": { "id": 1, "name": "Mars Rover" },
          "employee": { "id": 1, "name": "Mario" },
          "date": "2021-08-26T22:00:00.000Z",
          "hours": 5
        },
        {
          "project": { "id": 2, "name": "Manhattan" },
          "employee": { "id": 2, "name": "Giovanni" },
          "date": "2021-08-30T22:00:00.000Z",
          "hours": 3
        },
        {
          "project": { "id": 1, "name": "Mars Rover" },
          "employee": { "id": 1, "name": "Mario" },
          "date": "2021-08-31T22:00:00.000Z",
          "hours": 3
        },
        {
          "project": { "id": 1, "name": "Mars Rover" },
          "employee": { "id": 3, "name": "Lucia" },
          "date": "2021-08-31T22:00:00.000Z",
          "hours": 3
        },
        {
          "project": { "id": 2, "name": "Manhattan" },
          "employee": { "id": 1, "name": "Mario" },
          "date": "2021-08-26T22:00:00.000Z",
          "hours": 2
        },
        {
          "project": { "id": 2, "name": "Manhattan" },
          "employee": { "id": 2, "name": "Giovanni" },
          "date": "2021-08-31T22:00:00.000Z",
          "hours": 4
        }
      ]
    }
    

    This is how I want the data to be after carrying out the sum

          projects = [
    {project: "Mars Rover", employee: "Mario" hours: 8},
    {project: "Mars Rover", employee: "Lucia" hours: 3},
    {project: "Manhattan", employee: "Giovanni" hours: 7},
    {project: "Manhattan", employee: "Mario" hours: 2}
    ]
    

    In practice I want to group the data by Project Name and Employee Name and, in the case of an existing project and employee, add the hours. This is the code I wrote:

    let newRows = [];
    
      projects.forEach((element) => {
        if (newRows.length === 0) {
          newRows.push(element);
        } else {
          const index = newRows.findIndex(
            (project) =>
              project.project.id === element.project.id &&
              project.employee.id === element.employee.id
          );
          if (index > -1) {
            newRows[index].hours += element.hours;
          } else {
            newRows.push(element);
          }
        }
      });
    

    It works, but I rightfully get the same bug as the first example. Basically, every time I write code in that file, the total hours on the screen increase by themselves, then I reload the page and everything goes back to the way it was.

    enter image description here


  2. You can do the following:

    let hours = {};
    projects.map(p => {
        if (hours[p.project.name]) hours[p.project.name] += p.hours
        else hours[p.project.name] = p.hours
    });
    

    In the case of your projects, hours will look like this:
    enter image description here

    Once you have the total hours by project, you can for sure format it as you want, for instance you requested it like this:

    let projectsHours = []
    Object.keys(hours).map(name => {
        projectsHours.push({project: name, hours: hours[name]})
    })
    

    Complete code to run here:

    let projects = [
      {
        project: { id: 1, name: "Mars Rover" },
        employee: { id: 1, name: "Mario" },
        date: "2021-08-26T22:00:00.000Z",
        hours: 5
      },
      {
        project: { id: 2, name: "Manhattan" },
        employee: { id: 2, name: "Giovanni" },
        date: "2021-08-30T22:00:00.000Z",
        hours: 3
      },
      {
        project: { id: 1, name: "Mars Rover" },
        employee: { id: 1, name: "Mario" },
        date: "2021-08-31T22:00:00.000Z",
        hours: 3
      },
      {
        project: { id: 1, name: "Mars Rover" },
        employee: { id: 3, name: "Lucia" },
        date: "2021-08-31T22:00:00.000Z",
        hours: 3
      },
      {
        project: { id: 2, name: "Manhattan" },
        employee: { id: 1, name: "Mario" },
        date: "2021-08-26T22:00:00.000Z",
        hours: 2
      },
      {
        project: { id: 2, name: "Manhattan" },
        employee: { id: 2, name: "Giovanni" },
        date: "2021-08-31T22:00:00.000Z",
        hours: 4
      }
    ];
    
    let hours = {};
    projects.map((p) => {
      if (hours[p.project.name]) hours[p.project.name] += p.hours;
      else hours[p.project.name] = p.hours;
    });
    
    let projectsHours = [];
    Object.keys(hours).map((name) => {
      projectsHours.push({ project: name, hours: hours[name] });
    });
    
    
    console.log(projectsHours);
    Login or Signup to reply.
  3. Whenever the code runs it continues adding to the newRows array, instead of recalculating the data from scratch. In addition, to update rows, you need to find them, which makes the code less optimal.

    Reduce the projects to a Map, and then convert the Map back to an array using Array.from():

    const projects = [{"project":{"id":1,"name":"Mars Rover"},"employee":{"id":1,"name":"Mario"},"date":"2021-08-26T22:00:00.000Z","hours":5},{"project":{"id":2,"name":"Manhattan"},"employee":{"id":2,"name":"Giovanni"},"date":"2021-08-30T22:00:00.000Z","hours":3},{"project":{"id":1,"name":"Mars Rover"},"employee":{"id":1,"name":"Mario"},"date":"2021-08-31T22:00:00.000Z","hours":3},{"project":{"id":1,"name":"Mars Rover"},"employee":{"id":3,"name":"Lucia"},"date":"2021-08-31T22:00:00.000Z","hours":3},{"project":{"id":2,"name":"Manhattan"},"employee":{"id":1,"name":"Mario"},"date":"2021-08-26T22:00:00.000Z","hours":2},{"project":{"id":2,"name":"Manhattan"},"employee":{"id":2,"name":"Giovanni"},"date":"2021-08-31T22:00:00.000Z","hours":4}]
    
    const result = Array.from(projects.reduce((acc, o) => {
      const project = o.project.name
      
      if(!acc.has(project)) acc.set(project, { project, hours: 0 })
      
      acc.get(project).hours += o.hours
      
      return acc;
    }, new Map()).values())
    
    console.log(result)
    Login or Signup to reply.
  4. You can first create an object which looks like this using a reducer. The key of this object is the ID of the project.

    {
      "1": { "name": "Mars Rover", "hours": 11 },
      "2": { "name": "Manhattan", "hours": 9 }
    }
    

    Then you can throw that object into Object.values which will convert it into an array for you:

    [
        { name: 'Mars Rover', hours: 11 },
        { name: 'Manhattan', hours: 9 },
    ]
    

    It looks nicer without the comments, but broke it down for you.

    const result = Object.values(
        projects.reduce(
            (acc, curr) => ({
                ...acc, // Expand existing object
                // Add new key to this object using the project ID as a string
                [curr.project.id.toString()]: {
                    name: curr.project.name,
                    hours: // Get the current hours from the project
                           // or default to zero if not yet created
                           (acc[curr.project.id.toString()]?.hours || 0)
                           // Add the current project's hours to it
                           + curr.hours,
                },
            }),
            {},
        ),
    );
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search