Home > Enterprise >  filter array of objects and display grouped table data javascript
filter array of objects and display grouped table data javascript

Time:02-15

I wanted to display the table as follows. So I created the following component as shown below named envTableComponent. This component loops through the envResponse and displays the project name and build.

Image 1

export const envTableComponent = () => {
    let envTable = [];
    const headerTable = ['PROJECT NAME', 'BUILD']
    
    const envResponse = [
        {
          "environment": "PROJECT 1",
          "verticals": [
            {
              "projects": [
                {
                  "name": "ITALY",
                  "runs": [
                    {
                      "build": "B2345",
                    }
                  ]
                }
              ]
            }
          ]
        },
        {
          "environment": "PROJECT 2",
          "verticals": [
            {
              "projects": [
                {
                  "name": "FRANCE",
                  "runs": [
                    {
                      "build": "A4322",
                    }
                  ]
                },
                {
                    "name": "MOSCOW",
                    "runs": [
                      {
                        "build": "C3456",
                      }
                    ]
                  }
              ]
            }
          ]
        },
      ],

    const projects = [];
        envResponse.forEach((envMetric) => {
        envMetric.verticals.forEach((vertical) => {
            vertical.projects.forEach((project) => {
            const obj = {};
            obj.projectName = project.name;
            obj.build = project.runs[0].build;
            projects.push(obj);
            });
        });
        });
        envTable = projects

    return (
        <>
        <div>ENVIRONMENT</div>
        <table>
            <thead>
                <tr>
                    {headerTable && headerTable.map
                    ((header, indx) => {
                        return (<th key={indx}>{header}</th>)
                    })}
                </tr>
            </thead>
            <tbody>
                {envTable && envTable.length > 0 && envTable.map
                ((row, indx) => {
                    return (<tr key={indx}>
                    <td>{row.projectName}</td>
                    <td>{row.build}</td>
                    </tr>)
                })}
            </tbody>
        </table>
        </>
    )
}

Now, i am trying to do a group by environment and then display the projects for each environment. i am expecting my final result to be something like this shown in image below.

Can someone please let me know what changes i have to do in order to show the group by table form for my data.

enter image description here

CodePudding user response:

You're actually quite close: all you need is to actually create a dictionary where you have key-value pairs where the key is the environment and the value is simply the array of projects associated with that environment. If we slightly repurpose your forEach logic to use Array.prototype.reduce, we can get the structure you want:

const projectsByEnv = envResponse.reduce((acc, envMetric) => {
  const { environment, verticals } = envMetric;
  const projects = verticals.flatMap((vertical) => {
    return vertical.projects.map((project) => {
      return {
        projectName: project.name,
        build: project.runs[0].build
      };
    });
  });

  if (acc[environment]) {
    acc[environment].push(...projects);
  } else {
    acc[environment] = projects;
  }
  return acc;
}, {});

The projectsByEnv will simply have the following format: {PROJECT 1: Array(1), PROJECT 2: Array(2)}. To output it in your template, we need to step through the object using Object.entries(), and use the colspan trick to ensure the environment spans two columns:

<tbody>
  {Object.entries(projectsByEnv).map(([environment, projects]) => {
    return (
      <>
        <tr>
          <td colspan="2">{environment}</td>
        </tr>
        {projects.map((project) => {
          return (
            <tr>
              <td>{project.projectName}</td>
              <td>{project.build}</td>
            </tr>
          );
        })}
      </>
    );
  })}
</tbody>

With a bit of a CSS trick as well:

td[colspan] {
  border-top: 1px dashed black;
  border-bottom: 1px dashed black;
}

This is how it looks like after making the above changes:

enter image description here

You can see the full proof-of-concept example on CodeSandbox:

Edit epic-glade-f36go

  • Related