53

In JavaScript, is it possible to generate an HTML table from a 2D array? The syntax for writing HTML tables tends to be very verbose, so I want to generate an HTML table from a 2D JavaScript array, as shown:

[
  ["row 1, cell 1", "row 1, cell 2"], 
  ["row 2, cell 1", "row 2, cell 2"]
]

would become:

<table border="1">
  <tr>
    <td>row 1, cell 1</td>
    <td>row 1, cell 2</td>
  </tr>
  <tr>
    <td>row 2, cell 1</td>
    <td>row 2, cell 2</td>
  </tr>
</table>

So I'm trying to write a JavaScript function that would return a table from a 2D JavaScript array, as shown:

function getTable(array){
  // take a 2D JavaScript string array as input, and return an HTML table.
}
2
  • 14
    @VoronoiPotato I posted an answer to my own question so that the answer would be useful to other web developers - that's what Stack Overflow is for. There's even an "answer your own question" button that is shown when posting a question on the Stack Exchange Network. Commented Mar 1, 2013 at 18:56
  • 23
    @VoronoiPotato, self answered questions are welcome here - you may have issues with the question itself, but the ability to instantly answer it should not be one of them. blog.stackoverflow.com/2012/05/encyclopedia-stack-exchange Commented Mar 2, 2013 at 17:00

18 Answers 18

87

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"]]);
Sign up to request clarification or add additional context in comments.

6 Comments

This works, but it's a bit more verbose than the other answers that I've found.
The dom is verbose, but safer than innerHTML.
It appears that your solution generates a paragraph of text instead of generating a table: jsfiddle.net/jCVmZ
Anderson your answer is a stringbuilder, please spare us. His answer is totally correct and does not generate a paragraph. It makes a table exactly as described. (if you don't believe it, right click and inspect element). This is much more correct than your answer or daniel's. You should be using the DOM, not some html string injection.
Why does this function inject directly to document.body instead of returning the DOM object, as per OP's request? Where to inject the table should not be the responsibility of a createTable, unless you also provide an entry point as an argument.
|
46

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;
}

3 Comments

This does not deal well with strings that contain < or &.
Use the DOM, Luke!
Cross-site scripting!
9

Here's a version using template literals. It maps over the data creating new arrays of strings build from the template literals, and then adds them to the document with insertAdjacentHTML:

let data = [
  ['Title', 'Artist', 'Duration', 'Created'],
  ['hello', 'me', '2', '2019'],
  ['ola', 'me', '3', '2018'],
  ['Bob', 'them', '4.3', '2006']
];

function getCells(data, type) {
  return data.map(cell => `<${type}>${cell}</${type}>`).join('');
}

function createBody(data) {
  return data.map(row => `<tr>${getCells(row, 'td')}</tr>`).join('');
}

function createTable(data) {

  // Destructure the headings (first row) from
  // all the rows
  const [headings, ...rows] = data;

  // Return some HTML that uses `getCells` to create
  // some headings, but also to create the rows
  // in the tbody.
  return `
    <table>
      <thead>${getCells(headings, 'th')}</thead>
      <tbody>${createBody(rows)}</tbody>
    </table>
  `;
}

// Bang it altogether
document.body.insertAdjacentHTML('beforeend', createTable(data));
table { border-collapse: collapse; }
tr { border: 1px solid #dfdfdf; }
th, td { padding: 2px 5px 2px 5px;}

2 Comments

I like the template approach here as it works in so many other scenarios. Delving into your answer was kinda like peeling an onion. :)
Thanks. I think a lot of problems come from code trying to do much at the same time. Separating out the concerns means you really only have to think about one thing at a time, and means you can test it better.
6

Another innerHTML-less version.

function makeTable(array) {
    var table = document.createElement('table');
    for (var i = 0; i < array.length; i++) {
        var row = document.createElement('tr');
        for (var j = 0; j < array[i].length; j++) {
            var cell = document.createElement('td');
            cell.textContent = array[i][j];
            row.appendChild(cell);
        }
        table.appendChild(row);
    }
    return table;
}

2 Comments

Is there a better way than a nested loop?
Use innerHTML if the array contains HTML or entities
5

One-liner using es6 reduce

function makeTableHTML(ar) {
  return `<table>${ar.reduce((c, o) => c += `<tr>${o.reduce((c, d) => (c += `<td>${d}</td>`), '')}</tr>`, '')}</table>`
}

2 Comments

perfectly working but what if i want to add a class in <td class="yes">${d}</td> on some condition..i means how can i use if condition for TD
@FrederickaHartman, I have a similar (I too used a couple of reduces) answer posted, but I did not use any HTML in the JS code. For creating the tds I used Object.assign(document.createElement('td'), { textContent: cell }). If you want to add a class attribute you can include the className key in the object Object.assign(document.createElement('td'), { textContent: cell, className: 'yes' })
4

An es6 version of Daniel Williams' answer:

  function get_table(data) {
    let result = ['<table border=1>'];
    for(let row of data) {
        result.push('<tr>');
        for(let cell of row){
            result.push(`<td>${cell}</td>`);
        }
        result.push('</tr>');
    }
    result.push('</table>');
    return result.join('\n');
  }

Comments

3

Based on the accepted solution:

function createTable (tableData) {
  const table = document.createElement('table').appendChild(
    tableData.reduce((tbody, rowData) => {
      tbody.appendChild(
        rowData.reduce((tr, cellData) => {
          tr.appendChild(
            document
              .createElement('td')
              .appendChild(document.createTextNode(cellData)).parentNode
          )
          return tr
        }, document.createElement('tr'))
      )
      return tbody
    }, document.createElement('tbody'))
  ).parentNode

  document.body.appendChild(table)
}

createTable([
  ['row 1, cell 1', 'row 1, cell 2'],
  ['row 2, cell 1', 'row 2, cell 2']
])

With a simple change it is possible to return the table as HTML element.

1 Comment

I like it, but it doesn't include the border="1" table attribute specified.
2

See the fiddle demo to create a table from an array.

function createTable(tableData) {
  var table = document.createElement('table');
  var row = {};
  var cell = {};

  tableData.forEach(function(rowData) {
    row = table.insertRow(-1); // [-1] for last position in Safari
    rowData.forEach(function(cellData) {
      cell = row.insertCell();
      cell.textContent = cellData;
    });
  });
  document.body.appendChild(table);
}

You can use it like this

var tableData = [["r1c1", "r1c2"], ["r2c1", "r2c2"], ["r3c1", "r3c2"]];
createTable(tableData);

Comments

2

Generate table and support HTML as input.

Inspired by @spiny-norman https://stackoverflow.com/a/15164796/2326672

And @bornd https://stackoverflow.com/a/6234804/2326672

function escapeHtml(unsafe) {
    return String(unsafe)
         .replace(/&/g, "&amp;")
         .replace(/</g, "&lt;")
         .replace(/>/g, "&gt;")
         .replace(/"/g, "&quot;")
         .replace(/'/g, "&#039;");
 }

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++){
            k = escapeHtml((myArray[i][j]));
            result += "<td>"+k+"</td>";
        }
        result += "</tr>";
    }
    result += "</table>";

    return result;
}

Test here with JSFIDDLE - Paste directly from Microsoft Excel to get table

Comments

2

Pure functional table without new lines (Just for fun)

const pureFunctionalTable = data => 
    [document.createElement('table')].filter(table => !table.appendChild(
        data.reduce((tbody, row) =>
            !tbody.appendChild(row.reduce((tr, cell) =>
                !tr.appendChild(document.createElement('td'))
                   .appendChild(document.createTextNode(cell)) || tr
                , document.createElement('tr'))
            ) || tbody, document.createElement('tbody'))) || table)[0];


Usage

document.body.appendChild(pureFunctionalTable([
    ['row 1, cell 1', 'row 1, cell 2'],
    ['row 2, cell 1', 'row 2, cell 2']
]));

1 Comment

I like it, but it doesn't include the border="1" table attribute specified.
2

let data = [
  ['Title', 'Artist', 'Duration', 'Created'],
  ['hello', 'me', '2', '2019'],
  ['ola', 'me', '3', '2018'],
  ['Bob', 'them', '4.3', '2006']
];

function getCell (cell, type='td') {
        return `<${type}>${cell}</${type}>`
}
function getCells(cells, type='td') {
        return cells.map(cell => getCell(cell, type)).join('');
}

function getRow(row) {
        return `<tr> ${getCell(row[0], 'th')} ${getCells(row.slice(1))} </tr>`
} 
          
function createTable(data) {
  const [headings, ...rows] = data;
      
    return `
          <table>
            <thead>${getCells(headings, 'th')}</thead>
            <tbody>${rows.map(getRow).join('')}</tbody>
          </table>
    `;
}

document.body.insertAdjacentHTML('beforeend', createTable(data));
table { border-collapse: collapse; }
tr { border: 1px solid #dfdfdf; }
th, td { padding: 4px;}

This is the exact copy of @Andy's answer with a slight modification so that the first cell of every row will be th.

Comments

2

For those who do not want to use DOM

function test_makeTableHTML() {
  var array = [
    ['num', 'date', 'text'],
    [1, new Date(), 'foo'],
    [2, new Date(), 'bar'],
  ]
  var htmltable = makeTableHTML_(array);
  console.log(htmltable);
}

/**
 * creates HTML table code
 * ⚠️ not a DOM-element!
 * from 2d array with a header
 * 
 */
function makeTableHTML_(array) {
    var result = "<table border='1' style='border-collapse:collapse'><tr>";
    var header = array[0];
    for (var i = 0; i < header.length; i++) {
      result += "<th>"+header[i]+"</th>";
    }
    result += "</tr>";
    var val;
    for(var i = 1; i<array.length; i++) {
        result += "<tr>";
        for(var j=0; j<array[i].length; j++){
          val = array[i][j];
          if (val instanceof Date) {
            val = formatDate_(val);
          }
            result += "<td>"+val+"</td>";
        }
        result += "</tr>";
    }
    result += "</table>";

    return result;
}
/**
 * converts JS date
 * to human's date
 * 
 */
// https://stackoverflow.com/a/34015511/5372400
function formatDate_(date) {
  var options = { 
    weekday: 'long', 
    year: 'numeric', 
    month: 'long', 
    day: 'numeric' };
  return date.toLocaleDateString("en-US", options);
}

tested with https://html5-editor.net

Comments

0

I know this is an old question, but for those perusing the web like me, here's another solution:

Use replace() on the commas and create a set of characters to determine the end of a row. I just add -- to end of the internal arrays. That way you don't have to run a for function.

Here's a JSFiddle: https://jsfiddle.net/0rpb22pt/2/

First, you have to get a table inside your HTML and give it an id:

<table id="thisTable"><tr><td>Click me</td></tr></table>

Here's your array edited for this method:

thisArray=[["row 1, cell 1__", "row 2, cell 2--"], ["row 2, cell 1__", "row 2, cell 2"]];

Notice the added -- at the end of each array.

Because you also have commas inside of arrays, you have to differentiate them somehow so you don't end up messing up your table- adding __ after cells (besides the last one in a row) works. If you didn't have commas in your cell, this step wouldn't be necessary though.

Now here's your function:

function tryit(){
  document
    .getElementById("thisTable")
    .innerHTML="<tr><td>"+String(thisArray)
    .replace(/--,/g,"</td></tr><tr><td>")
    .replace(/__,/g,"</td><td>");
}

It works like this:

  1. Call your table and get to setting the innerHTML. document.getElementById("thisTable").innerHTML
  2. Start by adding HTML tags to start a row and data. "<tr><td>"
  3. Add thisArray as a String(). +String(thisArray)
  4. Replace every -- that ends up before a new row with the closing and opening of data and row. .replace(/--,/g,"</td></tr><tr><td>")
  5. Other commas signify separate data within rows. So replace all commas the closing and opening of data. In this case not all commas are between rows because the cells have commas, so we had to differentiate those with __: .replace(/__,/g,"</td><td>"). Normally you'd just do .replace(/,/g,"</td><td>").

As long as you don't mind adding some stray characters into your array, it takes up a lot less code and is simple to implement.

2 Comments

Me from the future. I have learned much. Don't do this. Please.
But I'm keeping this for posterity and because it's hilarious. And to those who are still making hackey code like this: keep going, and good for you for figuring out ways where you don't see a better one yet!
0

This is holmberd answer with a "table header" implementation

function createTable(tableData) {
  var table = document.createElement('table');
  var header = document.createElement("tr");
  // get first row to be header
  var headers = tableData[0];

  // create table header
  headers.forEach(function(rowHeader){
    var th = document.createElement("th");
    th.appendChild(document.createTextNode(rowHeader));
    header.appendChild(th);
  });      
  console.log(headers);

  // insert table header 
  table.append(header);
  var row = {};
  var cell = {};

  // remove first how - header
  tableData.shift();
  tableData.forEach(function(rowData, index) {
    row = table.insertRow();
    console.log("indice: " + index);
    rowData.forEach(function(cellData) {
      cell = row.insertCell();
            cell.textContent = cellData;
    });
  });
  document.body.appendChild(table);
}

createTable([["row 1, cell 1", "row 1, cell 2"], ["row 2, cell 1", "row 2, cell 2"], ["row 3, cell 1", "row 3, cell 2"]]);

Comments

0

Here is an example of how you can generate and read data from a matrix m x n... in JavaScript

let createMatrix = (m, n) => {
      let [row, column] = [[], []],
          rowColumn = m * n
      for (let i = 1; i <= rowColumn; i++) {
        column.push(i)
        if (i % n === 0) {
          row.push(column)
          column = []
        }
      }
      return row
    }

let setColorForEachElement = (matrix, colors) => {
  let row = matrix.map(row => {
    let column = row.map((column, key) => {
      return { number: column, color: colors[key] }
    })
    return column
  })
  return row
} 

const colors = ['red', 'green', 'blue', 'purple', 'brown', 'yellow', 'orange', 'grey']
const matrix = createMatrix(6, 8)
const colorApi = setColorForEachElement(matrix, colors)

let table ='<table>'
colorApi.forEach(row => {
  table+= '<tr>'
    row.forEach(column =>  table += `<td style='background: ${column.color};'>${column.number}<td>` )
  table+='</tr>'
})
table+= '</table>'
document.write(table);

Comments

0

My 10cent with ar being the array:

'<table><tr>'+ar.map(e=>'<td>'+e.join('</td><td>')+'</td>').join('</tr><tr>')+'</tr></table>'

Comments

0

React JSX solution:

let array2d = [
  ["row 1, cell 1", "row 1, cell 2"], 
  ["row 2, cell 1", "row 2, cell 2"]
];

Use .map like so:

<table border="1">
{
array2d.map((array) => 
<tr>
<td>{array[0]}</td>
<td>{array[1]}</td>
</tr>
)}
</table>

2 Comments

it is quite simple in react because it is already parsing your closing and opening tag but in pure javascript, this solution will not work.
I gather this assumes that there will only ever be 2 tds per tr. The question does not specifically state only 2 columns or not, but it does ask for 'verbose', which I would assume means this could vary.
0

Here is an ES6 / DOM based / one-liner function. This is similar to other answers, but includes simplified and/or corrected code.

const getTable = array => array.reduce((table, row) => table.appendChild(row.reduce((tr, cell) => tr.appendChild(Object.assign(document.createElement('td'), { textContent: cell })).parentNode, document.createElement('tr'))).parentNode, document.createElement('table'));

const getTable = array => array.reduce((table, row) =>
    table.appendChild(row.reduce((tr, cell) =>
        tr.appendChild(Object.assign(document.createElement('td'), { textContent: cell })).parentNode,
        document.createElement('tr')
    )).parentNode,
    document.createElement('table')
);

const tableData = [
    ["row 1, cell 1", "row 1, cell 2"],
    ["row 2, cell 1", "row 2, cell 2"]
];

document.body.append(getTable(tableData));
table,
td {
    border: 1px solid;
}

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.