0

I have an object (equipmentTable) containing arrays of values for each column in a table. I am having trouble getting this table to render properly. I think I am pretty close.

Here is the object:

{
    "manufacturer": [
        "Google",
        "Apple"
    ],
    "modelNumber": [
        "123456",
        "36987"
    ],
    "serialNumber": [
        "889977",
        "558877"
    ]
}

And what I have tried:

{equipmentTable && 
                <table className="def-tbl">
                    <thead>
                        <th>Manufacturer</th>
                        <th>Model Number</th>
                        <th>Serial Number</th>
                    </thead>
                    <tbody>
                        {console.log(equipmentTable)}
                        {equipmentTable.manufacturer.map((value) => (
                            <tr>
                                <td>
                                    {value}
                                </td>
                            </tr>
                        ))}
                        {equipmentTable.serialNumber.map((value) => (
                            <tr>
                                <td>
                                    {value}
                                </td>
                            </tr>
                        ))}
                    </tbody>
                </table>}

No matter what I try, everything renders in the first column.

Any help is appreciated!

1
  • 2
    you're defining a new row in each iteration. Move the <tr>s outside of the map() calls (though it looks like you'll need to access each prop value by index.) Commented Jul 21, 2022 at 14:09

2 Answers 2

1

Your data structure is a bit odd for representing a table. I recommend you transform the dataset into a matrix for easy mapping to table rows:

const toDataRows = (map) =>
  (keys => map[keys[0]].map((_, i) =>
    keys.map(k => map[k][i])))
  (Object.keys(map));

First gather the keys of the map. Keep in mind, the order will not be guaranteed to match the order of the columns in the HTML. Now map the first item (manufacture array) in the map and locate the corresponding data item at the key and index.

Note: Don't forget the <tr> inside the <thead>.

Working Demo

const { useState } = React;

const toDataRows = (map) =>
  (keys => map[keys[0]].map((_, i) =>
    keys.map(k => map[k][i])))
  (Object.keys(map));

const equipmentTable = {
  "manufacturer" : [ "Google" , "Apple"  ],
  "modelNumber"  : [ "123456" , "36987"  ],
  "serialNumber" : [ "889977" , "558877" ]
};

const EquipmentTable = (props) => {
  const { data } = props;
  return (
    <table className="def-tbl">
      <thead>
        <tr>
          <th>Manufacturer</th>
          <th>Model Number</th>
          <th>Serial Number</th>
        </tr>
      </thead>
      <tbody>
        {data.map((row) => (
          <tr key={row[0]}>
            {row.map((item) => (
              <td key={item}>{item}</td>
            ))}
          </tr>
        ))}
    </tbody>
  </table>
  );
};

const App = () => (
  <div>
    <h1>Equipment App</h1>
    <EquipmentTable data={toDataRows(equipmentTable)} />
  </div>
);

ReactDOM.createRoot(document.querySelector('#root')).render(<App />);
.def-tbl { border-collapse: collapse }
table, th, td { border: thin solid grey; }
th, td { padding: 0.25em; }
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.1.0/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.1.0/umd/react-dom.development.js"></script>


Advanced solution

In the example below, the original data is transformed into a list of object called "records". An array of column definitions i.e. columnDefs are also passed into the Table. We only need these two things to render a complete table.

const { useState } = React;

const toRecords = (map) =>
  (keys => map[keys[0]].map((_, i) =>
    keys.reduce((a, k) =>
      ({ ...a, [k]: map[k][i] }), {})))
  (Object.keys(map));

const Table = (props) => {
  const { columnDefs, dataSet } = props;
  return (
    <table className="def-tbl">
      <thead>
        <tr>
          {columnDefs.map(({ field, headerName }) => (
            <th data-field={field} key={field}>
              {headerName}
            </th>
          ))}
        </tr>
      </thead>
      <tbody>
        {dataSet.map((record, index) => (
          <tr key={record[columnDefs[0].field]}>
            {columnDefs.map(({ field }) => (
              <td key={record[field]}>
                {record[field]}
              </td>
            ))}
          </tr>
        ))}
      </tbody>
    </table>
  );
};

const equipmentData = {
  "manufacturer" : [ "Google" , "Apple"  ],
  "modelNumber"  : [ "123456" , "36987"  ],
  "serialNumber" : [ "889977" , "558877" ]
};

const columnDefs = [
  { field: 'manufacturer' , headerName: 'Manufacturer' },
  { field: 'modelNumber'  , headerName: 'Model Number' },
  { field: 'serialNumber' , headerName: 'Serial Number' },
]

const App = () => (
  <div>
    <h1>Equipment App</h1>
    <Table
      columnDefs={columnDefs}
      dataSet={toRecords(equipmentData)}
    />
  </div>
);

ReactDOM.createRoot(document.querySelector('#root')).render(<App />);
.def-tbl { border-collapse: collapse }
table, th, td { border: thin solid grey; }
th, td { padding: 0.25em; }
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.1.0/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.1.0/umd/react-dom.development.js"></script>

Sign up to request clarification or add additional context in comments.

Comments

0

It's a little unclear the output you want, but the main problem is that you are defining a new row on each iteration for each property. Moving the <tr> elements outside of the map() calls would be the logical step if your data was shaped per row.

But given the structure of your data it looks like you'll want to access the columns by index in a single map.

{
  equipmentTable && (
    <table className='def-tbl'>
      <thead>
        <tr>
          <th>Manufacturer</th>
          <th>Model Number</th>
          <th>Serial Number</th>
        </tr>
      </thead>
      <tbody>
        {equipmentTable.manufacturer.map((_, i) => (
          <tr key={`row_${i}`}>
            <td>{equipmentTable.manufacturer[i]}</td>
            <td>{equipmentTable.modelNumber[i]}</td>
            <td>{equipmentTable.serialNumber[i]}</td>
          </tr>
        ))}
      </tbody>
    </table>
  );
}

Or, to make it dynamic you could use nested map() calls on the Object.keys() and Object.values() of your data for the headers and body accordingly.

{
  equipmentTable && (
    <table className='def-tbl'>
      <thead>
        <tr>
          {Object.keys(equipmentTable).map((header) => (
            <th key={header}>{header}</th>
          ))}
        </tr>
      </thead>
      <tbody>
        {Object.values(equipmentTable)[0].map((_, i) => (
          <tr key={`row${i}`}>
            {Object.values(equipmentTable).map((values, j) => (
              <td key={`row${i}_col${j}`}>{values[i]}</td>
            ))}
          </tr>
        ))}
      </tbody>
    </table>
  );
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.