skip to Main Content

I have data like below with random number of rows (three level) and I want loop this data to display it like table below

enter image description here
enter image description here

enter image description here

const myData = [
  {
    subject: "Math",
    class: [
      {
        name: "Math-01",
        results: [
          {student: "jame",score: "10"},
          {student: "kelvin",score: "4"},
          { student: "smith",score: "8"}
        ]
      },
      {
        name: "Math-02",
        results: [
          { student: "joe",score: "8"},
          {student: "yassou",score: "8"}
        ]
      }
    ]
  },
  {
    subject: "History",
    class: [
      {
        name: "History-01",
        results: [
          {student: "zoe", score: "6"},
          {student: "jame", score: "8"},
          {student: "bean", score: "9"}
        ]
      },
      {
        name: "History-02",
        results: [
          {student: "luffy", score: "10"},
        ]
      }
    ]
  },
  {
    subject: "literature",
    class: [
      {
        name: "literature-01",
        results: [
          {student: "nami", score: 5}
        ]
      },
      {
        name: "literature-02",
        results: [
          {student: "zoro", score: 3}
        ]
      },
      {
        name: "literature-03",
        results: [
          {student: "shank", score: 5}
        ]
      }
    ]
  }
]

I tried with this approach
https://codesandbox.io/s/material-ui-table-demo-with-row-span-dq1w6
but it only works for level 2

here is my code at level 2 and it work
enter image description here
enter image description here

and I continue apply this logic for level 3 and it’s not working properly
enter image description here
enter image description here

can anyone help me with another approach and I want the table to look like the first picture from myData.

(*)Note: myData can have much more data with different number of rows so i would like to be able to build merge cell table dynamically (but it’s still the same format)

thank you so much.

3

Answers


  1. I made this data table using a javascript. You can check and refer to it.

    function getData(){
        const myData = [
            {
                subject: "Math",
                class: [
                    {
                        name: "Math-01",
                        results: [
                            {student: "jame",score: "10"},
                            {student: "kelvin",score: "4"},
                            {student: "smith",score: "8"}
                        ]
                    },
                    {
                        name: "Math-02",
                        results: [
                            {student: "joe",score: "8"},
                            {student: "yassou",score: "8"}
                        ]
                    }
                ]
            },
                {
                    subject: "History",
                    class: [
                    {
                        name: "History-01",
                        results: [
                            {student: "zoe", score: "6"},
                            {student: "jame", score: "8"},
                            {student: "bean", score: "9"}
                        ]
                    },
                    {
                        name: "History-02",
                        results: [
                            {student: "luffy", score: "10"},
                        ]
                    }
                ]
            },
                {
                    subject: "literature",
                    class: [
                    {
                        name: "literature-01",
                        results: [
                            {student: "nami", score: 5}
                        ]
                    },
                        {
                        name: "literature-02",
                        results: [
                            {student: "zoro", score: 3}
                        ]
                    },
                    {
                        name: "literature-03",
                        results: [
                            {student: "shank", score: 5}
                        ]
                    }
                ]
            }
        ];
        let html ="<table border='1|1'>";
        html +=`
        <tr> 
            <th>Subject</th>
            <th>Class</th>
            <th>Student</th>
            <th>Score</th> 
        </tr>`;
        myData.forEach((item) => {
            let subject = item.subject;
            let row = subject == 'Math' ? 5 : subject == 'History' ? 4 : 3;
            html += `<th rowspan="${row}"> ${subject} </th>`;
            item.class.forEach((item2) => {
                html += `<th rowspan="${item2.results.length}">${item2.name}</th>`;
                item2.results.forEach((item3 => {
                    let name = item3.student;
                    let score = item3.score;
                    html += `<td>${name}</td><td>${score}</td></tr>`;
                }))
            })
        })
        html += "</table>";
        document.body.innerHTML = html;
    }
    <body onLoad="getData()">
    Login or Signup to reply.
  2. I have 0 experience in reactjs, but I can give you advice on how to structure your database to handle this efficiently. I’ve done this with another Javascript framework in the past which handles tree data.

    You are hardcoding your dataset with each level being a different type. The trick here is to soft code your dataset to handle the data dynamically. So if your data changes, your code does not have to change. If you want your dataset truly dynamic, I’d suggest changing your data model to what I have below. And if the database cannot change, you can write a view that queries your current data model to appear like the new data model.

    Create a table called node_hierarchy where you have columns parent_id, child_id and node_id. These values are arbitrary and can be anything unique when inserting your data into the database. The PK for this table can be the composite key of both parent_id and child_id. Or you can make a concat_id as the PK which is both strung together and use that single column as the PK rather than two columns (composite). And a second table called node with columns node_id, node_type, and node_value. This way you can add additional types and multiple levels without worrying about changing your source code. The node_id in each table becomes the association, one being the FK in the node table and PK in the node_hierarchy table.

    When you query your data, make sure the id of the node is the concatenation of the parent_id + child_id, IF you repeat a node in your tree (hierarchy). Meaning that node has multiple parents. If nodes do not have multiple parents, ignore this. If you do not concatenate the ids, you will have duplicate ids in the tree and may cause an exception when rendering. Often times, client tree ids need to be unique, so verify with reactjs. When building the dataset, add the concat_id to your node in the JSON data, but hide the concat_id when rendering. Use it for reference only.

    When querying the data, you can add the node_level, but also hide this in the rendered dataset. The level should not be in the database since this is dynamic based on the number of node_hierarchy records with unique parent_ids. You could even have another render_level, if this is different from the data set level (node_level) in the database.

    Example:

    node_type, node_value [, concat_id, node_level, render_level]
    
    ----------------------- node_level = 0 ; render_level (ignored in query) ---------
    root, Subject
    root, Class
    root, Student
    root, Score
    ----------- node_level = 1 ; render_level (ignored in query) ------------
    Subject, Math
    Subject, History
    Subject, Literature
    ------------------------ node_level = 2 ; render_level = 1 -------------
    Class, Math-01
    Class, Math-02
    etc...
    ------------------- node_level = 3 ; render_level = 2 ------------
    Student, Jame
    Student, Calvin
    etc...
    ------------------- node_level = 4 ; render_level = 3 --------------
    Score, 10
    Score, 4
    etc...
    

    Without knowing the type of database you are using, I can’t give you any code to write the recursive queries to pull the dataset. If you are using PostgreSQL for example, then do the following:

    Your query on the node_hierarchy (or node_tree if you call it that), should use WITH RECURSIVE to union the ancestors and decendents together into one dataset. The concat_id, node_level and render_level will be dynamic data column values. Join your node table to the node_hierarchy table with an INNER JOIN (node.node_id = node_hierarchy.node_id) to get the static data column values. And the context in which I’m using the words "dynamic" and "static" are based on retrieval of data. This allows your static data (of retrieval) in the database to become dynamic with the context of storage/maintenance of data.

    The JSON will look something like so. Realize that the JSON will have variables in the value part of the key/value pairs, but the rendered HTML of the JSON after interpolation will not (and will just have values). Example is this.nodes.node_value. This is just showing where the values are retrieved. Some JavaScript client frameworks allow variables in the JSON interpolation and some do not. Verify with ReactJS. I’m not interpolating the keys here. For example, name could be this.parent_node.node_value (or this.parent_node_value if it exists). Or for example, nodes could be defined based on level. So nodes would be "results" for level 2. So you can interpolate both key or value in some JS client frameworks.

    const myData = [
      {
        concat_id: "0-1",
        parent_id: "0",
        child_id: "1",
        render_level: "1",
        subject: node_value <---- dynamically becomes "Math"
        nodes: [
        {
            concat_id: "1-2",
            parent_id: "1",
            child_id: "2",
            render_level: "2",
            name: node_value, < ---- dynamically becomes "Math-01"
            nodes: [
            {
                concat_id: "2-3",
                parent_id: "2",
                child_id: "3",
                render_level: "3",
                student: node_value, <---------- becomes "Jame"
                score: this.nodes.node_value <--------- becomes "10"
            },
            {
                concat_id: "2-4",
                parent_id: "2",
                child_id: "4",
                render_level: "3",
                student: node_value, <---------- becomes "Kelvin"
                score: this.nodes.node_value <--------- becomes "4"
            },
            {
                concat_id: "2-5",
                parent_id: "2",
                child_id: "5",
                render_level: "3",
                student: node_value, <---------- becomes "Smith"
                score: this.nodes.node_value <--------- becomes "8"
            }    
            ]
            }
        ]
    ]
    

    In fact, node_type can be omitted. The node_type will have it’s own node. In the dataset I provided, the node_type will be the node_value of the parent node (ancestor) and the node_value will be the child node (descendent).

    Good luck my friend.

    Login or Signup to reply.
  3. const myData = [
      {
        subject: "Math",
        class: [
          {
            name: "Math-01",
            results: [
              {student: "jame",score: "10"},
              {student: "kelvin",score: "4"},
              { student: "smith",score: "8"}
            ]
          },
          {
            name: "Math-02",
            results: [
              { student: "joe",score: "8"},
              {student: "yassou",score: "8"}
            ]
          }
        ]
      },
      {
        subject: "History",
        class: [
          {
            name: "History-01",
            results: [
              {student: "zoe", score: "6"},
              {student: "jame", score: "8"},
              {student: "bean", score: "9"}
            ]
          },
          {
            name: "History-02",
            results: [
              {student: "luffy", score: "10"},
            ]
          }
        ]
      },
      {
        subject: "literature",
        class: [
          {
            name: "literature-01",
            results: [
              {student: "nami", score: 5}
            ]
          },
          {
            name: "literature-02",
            results: [
              {student: "zoro", score: 3}
            ]
          },
          {
            name: "literature-03",
            results: [
              {student: "shank", score: 5}
            ]
          }
        ]
      },
      {
        subject: "literature",
        class: [
          {
            name: "literature-01",
            results: []
          },
          {
            name: "literature-02",
            results: [ {student: "zoro", score: 3} ]
          },
          {
            name: "literature-03",
            results: [ {student: "shank", score: 5} ]
          }
        ]
      }
    ];
    
    function App() {
      return (
        <table>
          {
            myData.map((record, i) => {
              let subjectHandled = false;
              return record.class.map((course, j) => {
                return course.results.map((r, k) => {
                  let subjectTdEl;
                  
                  if (!subjectHandled && k === 0) {
                    let resultsCount = 0;
                    
                    record.class.forEach((c) => {
                      resultsCount += c.results.length;
                    });
                    
                    subjectTdEl = <td rowSpan={resultsCount}>{record.subject}</td>
                    
                    subjectHandled = true;
                  }
                  
                  let courseTdEl;
                  if (k === 0) {
                    courseTdEl = <td rowSpan={course.results.length}>{course.name}</td>;
                  }
                  
                  return (
                    <tr key={k}>
                      {subjectTdEl}       
                      {courseTdEl}
                      <td>{r.student}</td>
                      <td>{r.score}</td>
                    </tr>
                  );
                });
              });
            })
          }
        </table>
      );
    }
    
    ReactDOM.render(<App />, document.getElementById('root'));
    table {
      font-family: verdana;
      border-collapse: collapse;
    }
    
    table, th, td {
      padding: 10px;
      border: 1px solid black;
      text-align: center;
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
    
    <div id="root"></div>
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search