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.
}
Yukulélé
  • 15,644
  • 10
  • 70
  • 94
Anderson Green
  • 30,230
  • 67
  • 195
  • 328
  • 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. – Anderson Green Mar 01 '13 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. http://blog.stackoverflow.com/2012/05/encyclopedia-stack-exchange/ – Jaydles Mar 02 '13 at 17:00

17 Answers17

85

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"]]);
Community
  • 1
  • 1
bmavity
  • 2,477
  • 2
  • 24
  • 23
  • This works, but it's a bit more verbose than the other answers that I've found. – Anderson Green Mar 01 '13 at 19:16
  • 1
    The dom is verbose, but safer than innerHTML. – bmavity Mar 01 '13 at 19:22
  • It appears that your solution generates a paragraph of text instead of generating a table: http://jsfiddle.net/jCVmZ/ – Anderson Green Mar 01 '13 at 19:24
  • 12
    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. – VoronoiPotato Mar 01 '13 at 19:29
  • If the verbosity bothers you I suggest a library such as Jquery to simplify some of these method calls. – VoronoiPotato Mar 01 '13 at 19:32
  • 2
    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. – Magnus Bull Mar 03 '21 at 17:58
44

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;
}
Hugo Dozois
  • 8,147
  • 12
  • 54
  • 58
Daniel Williams
  • 8,673
  • 4
  • 36
  • 47
8

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;}
Andy
  • 61,948
  • 13
  • 68
  • 95
  • 1
    I like the template approach here as it works in so many other scenarios. Delving into your answer was kinda like peeling an onion. :) – Tony Sep 05 '21 at 16:30
  • 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. – Andy Sep 05 '21 at 16:46
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;
}
Spiny Norman
  • 63
  • 1
  • 4
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');
  }
Jack NUMBER
  • 446
  • 1
  • 5
  • 22
cs01
  • 5,287
  • 1
  • 29
  • 28
4

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>`
}
zoispag
  • 371
  • 1
  • 4
  • 8
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);
surfmuggle
  • 5,527
  • 7
  • 48
  • 77
holmberd
  • 2,393
  • 26
  • 30
2

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))
          )
          return tr
        }, document.createElement('tr'))
      )
      return tbody
    }, document.createElement('tbody'))
  )

  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.

Romul81
  • 21
  • 3
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

Punnerud
  • 7,195
  • 2
  • 54
  • 44
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.

Ali Khosro
  • 1,580
  • 18
  • 25
1

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']
]));
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.

monsto
  • 1,178
  • 1
  • 13
  • 26
Josh Powlison
  • 723
  • 9
  • 15
  • Me from the future. I have learned much. Don't do this. Please. – Josh Powlison Jul 24 '21 at 13:28
  • 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! – Josh Powlison Jul 24 '21 at 13:30
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"]]);

jcom
  • 91
  • 1
  • 9
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);
Driton Haxhiu
  • 117
  • 1
  • 6
0

My 10cent with ar being the array:

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

chhu79
  • 175
  • 2
  • 7
0

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

Max Makhrov
  • 17,309
  • 5
  • 55
  • 81
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>
  • 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. – jerryurenaa Jan 20 '23 at 17:00