Here's a function that will use the dom instead of string concatenation.
function createTable(tableData) {
var table = document.createElement('table');
var tableBody = document.createElement('tbody');
tableData.forEach(function(rowData) {
var row = document.createElement('tr');
rowData.forEach(function(cellData) {
var cell = document.createElement('td');
cell.appendChild(document.createTextNode(cellData));
row.appendChild(cell);
});
tableBody.appendChild(row);
});
table.appendChild(tableBody);
document.body.appendChild(table);
}
createTable([["row 1, cell 1", "row 1, cell 2"], ["row 2, cell 1", "row 2, cell 2"]]);
Answer from bmavity on Stack OverflowHere's a function that will use the dom instead of string concatenation.
function createTable(tableData) {
var table = document.createElement('table');
var tableBody = document.createElement('tbody');
tableData.forEach(function(rowData) {
var row = document.createElement('tr');
rowData.forEach(function(cellData) {
var cell = document.createElement('td');
cell.appendChild(document.createTextNode(cellData));
row.appendChild(cell);
});
tableBody.appendChild(row);
});
table.appendChild(tableBody);
document.body.appendChild(table);
}
createTable([["row 1, cell 1", "row 1, cell 2"], ["row 2, cell 1", "row 2, cell 2"]]);
This is pretty easy to do with a double for loop.
function makeTableHTML(myArray) {
var result = "<table border=1>";
for(var i=0; i<myArray.length; i++) {
result += "<tr>";
for(var j=0; j<myArray[i].length; j++){
result += "<td>"+myArray[i][j]+"</td>";
}
result += "</tr>";
}
result += "</table>";
return result;
}
algorithm - Generate an HTML table using JavaScript from an array of objects - Code Review Stack Exchange
How to create a table from an array using javascript - Stack Overflow
html - Convert table to array in JavaScript without using jQuery - Stack Overflow
Print out Javascript array in table - Stack Overflow
Videos
Use the DOM APIs
For performance avoid adding markup (HTML) to the page via JavaScript. The DOM APIs are much faster and can be abstracted to make more readable code (DOM APIs are very verbose).
Source complexity
Your solution is convoluted and hard to read. It looks like you tackled the whole problem in one big step. More than a dozen lines of code should be seen as more than one problem to solve.
To solve complex or multi step problem break them into smaller single role parts.
Find all rows names
Find all columns names
Find value by row column name
Create DOM elements
Append a DOM elements
Create a table.
Each of these sub problems can then be solved by defining a function. When you have all the functions you can solve the main problem by combining the sub problems.
Try to make the functions generic. Property names should be dynamic so that you need only minor changes when the data changes. The example show 4 tables, to change your code to provide the different tables would take a lot more work than adding 3 more calls to the main function.
Example
The rewrite breaks the problem into the pars described above. The title, and the names of the row and column properties are passed to the function.
You can see the flexibility of this approach as the table can easily be rotated and transformed by changing the argument order and properties to use for columns and rows.
const notes = [{note: "n1", subject: "subject1", value: 10 }, {note: "n2", subject: "subject2", value: 15 }, {note: "n2", subject: "subject2", value: 5 }, {note: "n3", subject: "subject2", value: 20 }];
const tag = (tagName, props = {}) => Object.assign(document.createElement(tagName), props);
const txtTag = (tagName, str, props = {}) => tag(tagName, {textContent: str, ...props});
const append = (el, ...sibs) => sibs.reduce((p, sib) => (p.appendChild(sib), p), el);
append(document.body,
createTable("Subjects", "note", "subject", "value", notes),
createTable("Notes", "subject", "note", "value", notes),
createTable("Notes", "value", "note", "value", notes),
createTable("Values", "subject", "value", "value", notes));
function createTable(title, colKey, rowKey, valKey, data) {
const byKey = key => [...new Set(data.map(rec => rec[key]))];
const byRowCol = (row, col) => data.reduce((val, rec) =>
val + (rec[rowKey] === row && rec[colKey] === col ? rec[valKey] : 0), 0);
const rows = byKey(rowKey), cols = byKey(colKey);
return append(tag("table"),
append(tag("tbody"),
append(tag("tr"),
txtTag("th", title),
...cols.map(str => txtTag("th", str))
),
...rows.map(row =>
append(tag("tr"),
txtTag("th", row),
...cols.map(col => txtTag("td", byRowCol(row, col)))
)
)
)
)
}
* {font-family: arial}
table, tr, th, td {
border-collapse: collapse;
border: 1px solid;
padding: 3px 10px;
margin: 4px;
}
A short review;
- JS should be lowerCamelCase, so
make_matrix->makeMatrix array_objsshould probably be just beobjects- I am not a big fan of HTML in JS, consider using a template
- In
makeMatrixyou both convert the data into a new structure and build the output, I would split this across 2 functions - I see a tendency to name variables more after what they are than what they contain, I hope my counter-examples shows what I mean
- I find your code very dense and hard to read
- However I really like the trick to create subjects and notes, will steal that ;)
let notes = [
{ note: "n1", subject: "subject1", value: 10 },
{ note: "n2", subject: "subject2", value: 15 },
{ note: "n2", subject: "subject2", value: 5 },
{ note: "n3", subject: "subject2", value: 20 },
];
function restructureSubjectScores(subjectScores){
const out = {};
for(const score of subjectScores){
out[score.subject] = out[score.subject] || {};
out[score.subject][score.note] = score.value;
}
return out;
}
function buildTable(noteScores){
const subjects = [...new Set(noteScores.map(({ subject }) => subject))];
const notes = [...new Set(noteScores.map(({ note }) => note))];
const tableData = restructureSubjectScores(noteScores);
const table = document.getElementById('tableTemplate').content.firstElementChild.cloneNode(true);
const header = table.querySelector('thead tr');
header.innerHTML += notes.map(column => `<th>${column}</th>`).join("");
const tbody = table.querySelector("tbody");
const rowTemplate = document.getElementById('rowTemplate').content.firstElementChild;
for(const subject of subjects){
const row = rowTemplate.cloneNode(true);
row.querySelector("th").textContent = subject;
for(const note of notes){
row.innerHTML += `<td>${tableData[subject][note] || 0}</td>`;
}
tbody.appendChild(row);
}
return table;
}
document.querySelector(".content").appendChild(buildTable(notes));
table {
border-collapse: collapse;
border: 1px solid;
}
tr,
th,
td {
border: 1px solid;
padding: 3px 10px;
}
<div class="content"></div>
<template id="tableTemplate">
<table>
<thead><tr>
<th class="subject">Subjects</th>
</tr></thead>
<tbody>
</tbody>
</table>
</template>
<template id="rowTemplate">
<tr>
<th class="subject"></th>
</tr>
</template>
You're currently trying to add a string to an table object. Check this page out.
function createTable() {
var headers = ["Title", "Author", "Read?"];
var table = document.createElement("TABLE"); //makes a table element for the page
for(var i = 0; i < books.length; i++) {
var row = table.insertRow(i);
row.insertCell(0).innerHTML = books[i].title;
row.insertCell(1).innerHTML = books[i].author;
row.insertCell(2).innerHTML = books[i].alreadyRead;
}
var header = table.createTHead();
var headerRow = header.insertRow(0);
for(var i = 0; i < headers.length; i++) {
headerRow.insertCell(i).innerHTML = headers[i];
}
document.body.append(table);
}
To add stringified tags to parent tag,
parentTag.innerHTML = stringifiedTag;
Hope it's helpful.
var books = [
{
title: 'The Stranger',
author: 'Albert Camus',
alreadyRead: true
},
{
title: 'Binging with Babish',
author: 'Andrew Rea',
alreadyRead: true
},
{
title: 'You Suck at Cooking: The Absurdly Practical Guide to Sucking Slightly Less at Making Food: A Cookbook',
author: 'You Suck at Cooking',
alreadyRead: false
}];
createTable();
function createTable() {
var table = document.createElement("table"); //makes a table element for the page
let innerT = "";
innerT += "<tr class='firstRow'><th>Title</th><th>Author</th><th>Read?</th></tr>"; //adds the first row that contains the sections for the table
for (var i = 0; i < books.length; i++) //loops through the array
{
//add info from the array into this
innerT += "<tr><td>" + books[i].title + "</td><td>";
}
table.innerHTML = innerT;
document.body.append(table);
}
With qSA and Array.prototype.map is pretty simple.
var tableInfo = Array.prototype.map.call(document.querySelectorAll('#tableId tr'), function(tr){
return Array.prototype.map.call(tr.querySelectorAll('td'), function(td){
return td.innerHTML;
});
});
Presuming your table is something like the one below, you can convert that into an array of arrays using the rows collection of the table and cells collection of the rows:
function tableToArray(table) {
var result = []
var rows = table.rows;
var cells, t;
// Iterate over rows
for (var i=0, iLen=rows.length; i<iLen; i++) {
cells = rows[i].cells;
t = [];
// Iterate over cells
for (var j=0, jLen=cells.length; j<jLen; j++) {
t.push(cells[j].textContent);
}
result.push(t);
}
return result;
}
document.write(JSON.stringify(tableToArray(document.getElementsByTagName('table')[0])));
<table>
<tr>
<td>one<td>two<td>three
<tr>
<td>one<td>two<td>three
<tr>
<td>one<td>two<td>three
</table>
Or if like concise code, use some ES5 goodness:
function tableToArray(table) {
var result = [].reduce.call(table.rows, function (result, row) {
result.push([].reduce.call(row.cells, function(res, cell) {
res.push(cell.textContent);
return res;
}, []));
return result;
}, []);
return result;
}
Using jQuery you can do:
var txt = '{"employees":[' +
'{"firstName":"John","lastName":"Doe" },' +
'{"firstName":"Anna","lastName":"Smith" },' +
'{"firstName":"Peter","lastName":"Jones" }]}';
// $.parseJSON will parse the txt (JSON) and convert it to an
// JavaScript object. After its call, it gets the employees property
// and sets it to the employees variable
var employees = $.parseJSON( txt ).employees;
var $table = $( "<table></table>" );
for ( var i = 0; i < employees.length; i++ ) {
var emp = employees[i];
var $line = $( "<tr></tr>" );
$line.append( $( "<td></td>" ).html( emp.firstName ) );
$line.append( $( "<td></td>" ).html( emp.lastName ) );
$table.append( $line );
}
$table.appendTo( document.body );
// if you want to insert this table in a div with id attribute
// set as "myDiv", you can do this:
$table.appendTo( $( "#myDiv" ) );
jsFiddle: http://jsfiddle.net/davidbuzatto/aDX7E/
var table = document.createElement("table");
for (var i = 0; i < employees.length; i++) {
var row = table.insertRow(-1);
var firstNameCell = row.insertCell(-1);
firstNameCell.appendChild(document.createTextNode(employees[i].firstName));
var lastNameCell = row.insertCell(-1);
lastNameCell.appendChild(document.createTextNode(employees[i].lastName));
}
document.body.appendChild(table);
I've interpreted your question as though you want to extract 1 zone from voting_section, that zone should have the highest number appended to it.
var data = [{
"votes": 200,
"invalid_votes": 140,
"valid_votes": 60,
"voting_section": {
"level": 1,
"zone1": "US",
"zone2": "Delaware"
}
},
{
"votes": 300,
"invalid_votes": 40,
"valid_votes": 260,
"voting_section": {
"level": 1,
"zone1": "US",
"zone2": "California",
"zone3": "Los Angeles"
}
}
],
html = "";
function getLastZone(voting_section) {
var highestZone = {
zoneNumber: null,
zoneText: null
};
for (var zone in voting_section) {
var checkZone = /zone(\d)/g.exec(zone);
if (checkZone) {
if (parseInt(checkZone[1]) > highestZone.zoneNumber) {
highestZone = {
zoneNumber: [checkZone[1]],
zoneText: voting_section[zone]
};
}
}
}
return highestZone.zoneText;
}
data.forEach(function(e, i) {
html += "<tr>" + "<td>" + getLastZone(e.voting_section) + "</td>" +
"<td>" + e.votes + "</td>" +
"<td>" + e.valid_votes + "</td>" +
"<td>" + e.invalid_votes + "</td>" + "</tr>";
})
document.getElementById("putHere").innerHTML = html;
table {
border-collapse: collapse;
border-left: 1px solid #bbbbbb;
border-top: 1px solid #bbbbbb;
}
th, td {
border-right: 1px solid #bbbbbb;
border-bottom: 1px solid #bbbbbb;
}
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Document</title>
</head>
<body>
<table>
<thead>
<th>Zone</th>
<th>Votes</th>
<th>Valid Votes</th>
<th>Invalid Votes</th>
</thead>
<tbody id="putHere"></tbody>
</table>
</body>
</html>
You could create one main function to build the table, from which you could call a row-building function (while iterating over the data rows). I've added an example to my post here.
var data = [{
"votes":200,
"invalid_votes":140,
"valid_votes":60,
"voting_section":{"level":2, "zone1":"US", "zone2":"Delaware"}
},
{
"votes":300,
"invalid_votes":40,
"valid_votes":260,
"voting_section":{"level":3, "zone1":"US", "zone2":"California", "zone3":"Los Angeles"}
}];
var buildTable = function(data, container){
/* Builds one data row into a <tr> element string */
var buildRow = function(rowData){
return `<tr><td>${rowData.voting_section.zone2}</td><td>${rowData.votes}</td><td>${rowData.valid_votes}</td><td>${rowData.invalid_votes}</td></tr>`;
}
/* Reduces the array of row data into one string */
var rows = data.reduce(function(rows, row){
return rows+buildRow(row);
}, '');
/* Creates the full table and includes the rows string */
container.innerHTML = `<table><thead><tr><td></td><td>Votes</td><td>Valid Votes</td><td>Invalid Votes</td></tr></thead><tbody>${rows}</tbody>`;
}
var resultsTableDiv = document.getElementById('results')
buildTable(data, resultsTableDiv);
<div id="results"></div>