Rakesh Adhikesavan
Rakesh Adhikesavan

Reputation: 12816

How to populate an HTML table from JavaScript Object?

I have the following Javascript object:

data = {
    "Q1": [1,2,3,2],
    "Q2": [5,6,7,6],
    "Q3": [9,10,11,10]
};

From this I am trying to generate the following Table:

+----------+---------+--------+
| Question | Options | Answer |
+----------+---------+--------+
| Q1       |       1 |      2 |
+          +---------+        +
|          |       2 |        |
+          +---------+        +
|          |       3 |        |
+----------+---------+--------+
| Q2       |       5 |      6 |
+          +---------+        +
|          |       6 |        |
+          +---------+        +
|          |       7 |        |
+----------+---------+--------+
| Q3       |       9 |     10 |
+          +---------+        +
|          |      10 |        |
+          +---------+        +
|          |      11 |        |
+----------+---------+--------+

Here is the logic to converting the Javascript object to the table: The key of the dictionary goes under column question, the first 3 values in the array of the value corresponding to the key goes under column options, the last value in the array goes under column answer.

For example, the first key value pair in the object is

"Q1": [1,2,3,2]

Q1 goes under column Question and the last value in the array [1,2,3,2] goes under the column Answer, the remaining three values in the array become three individual rows under the column Options.

+----------+---------+--------+
| Question | Options | Answer |
+----------+---------+--------+
| Q1       |       1 |      2 |
+          +---------+        +
|          |       2 |        |
+          +---------+        +
|          |       3 |        |
+----------+---------+--------+

Here is what I tried:

JSFiddle

Same snippet pasted below:

data = {
    "Q1": [1,2,3,2],
    "Q2": [5,6,7,6],
    "Q3": [9,10,11,10]
};

//console.log(data);
var tbody = document.getElementById('tbody');

tbody.innerHTML += "<tr>" + 
                   "<th id='q'>" + "Question" + "</th>" +
                   "<th id='o'>" + "Options" + "</th>" +
                   "<th id='ca'>" + "Correct Answer" + "</th>" +
                   "</tr>"
for (var question in data) {
var tr = "<tr>";
options = data[question];
answer = options.pop();

tr += "<td rowspan=" + options.length +" headers='q'>" + question + "</td>";
for(var index in options){
tr += "<td headers='o'>" + options[index] + "</td>"
}
tr +=  "<td headers='ca' rowspan=" + options.length + ">" + answer + "</td>" +
       "</tr>"
tbody.innerHTML += tr;
}
<table class="table">
    <tbody id="tbody"></tbody>
</table>

Upvotes: 1

Views: 3941

Answers (1)

Sphinx
Sphinx

Reputation: 10719

Below codes should meet your requirements.

My solution:

  1. loop the data first to re-shape your data, you can add some validators if necessary. (It seems the array includes options and answers, so assuming the last one always is the answer field), possible someone will feel this step is not neccessary, but what I thought is treat it as one adapter, for other different format data, we can create another adapter to format the data then doesn't need to modify the rest of the codes (generate HTML). Then add some validators will make the codes stronger.

  2. then loop each item to generate the rows (HTML) (for Q1, Q2, Q3 etc), the trick is uses options.length as the rowspan value.

  3. combine all HTMLs

data = {
    "Q1": [1,2,3,2],
    "Q2": [5,6,7,6],
    "Q3": [9,10,11,10],
    'Q4': [], // test case 1: no data
    'Q5': [1], // test case 2: only answer field
    'Q6': ['A', 'B', 'C'],// test case 3: for string
    'Q7': ['TEST'] // test case 4: for answer(string) only
};
// For Q4, Q5, you can added some extra codes to uses default values intead, or ignore this row.

// formmat the data first, add some validators if neccessary
function formatAdapter (data) {
  return Object.entries(data).map((item) => {
  let newItem = {}
  newItem[item[0]] = {'options': item[1].slice(0, item[1].length -1),
    'answer': item[-1]} // assuming the last element is the answer, [0:last] is the options
  return [item[0], item[1].slice(0, item[1].length -1), item[1].slice(-1)[0]]
})
}
let formmatedData = formatAdapter(data)

var tbody = document.getElementById('tbody');

// generate the header
tbody.innerHTML += "<tr>" + 
                   "<th id='q'>" + "Question" + "</th>" +
                   "<th id='o'>" + "Options" + "</th>" +
                   "<th id='ca'>" + "Correct Answer" + "</th>" +
                   "</tr>"
// generate the rows(Html) for each questions

let rowHtmls = formmatedData.map((item) => {
  let row = '<tr><td rowspan="'+(item[1].length || 1) +'">'+item[0]+'</td>'
            + '<td>'+item[1][0]+'</td>'
            + '<td rowspan="'+(item[1].length || 1)+'">'+item[2]+'</td></tr>'
  row += item[1].slice(1).reduce((pre, cur) => {
    return pre + '<tr><td>'+cur+'</td></tr>'
  }, '')
  return row
})

// combine header(html) and rows(html)
tbody.innerHTML += rowHtmls.reduce((pre, cur) => {
  return pre+cur
}, '')
table tbody tr td {
  border: 1px solid black
}
<table class="table">
    <tbody id="tbody"></tbody>
</table>

Upvotes: 2

Related Questions