How would one create a dynamic html table from a one dimensional array, taking into account rowspan and colspan?
<p>I need to construct a html table from a one dimensional array which, for abstractions sake, has the following format:</p>

<pre><code>{ value: "ABC", colspan: 1, rowspan: 2 }, // etc
</code></pre>

<p>There is also a property called <code>width</code> which will be dynamic and represent the number of columns. </p>

<p>The code below, I believe is close, and can handle "non-rowspan" data - but I am getting tripped up on how to account for cells spanning, without the table exceeding the column count. </p>

<p>I feel like I need a "stepper" which counts up and down everytime there is a rowspan, but I can't get the maths correct. </p>

<p>At the moment, any rowspan causes the next row to exit the right of the table. </p>

<p>Essentially I would like it to wrap and drop each one in the next available spot. In otherwords assmeble the table dynamically. </p>

<h1>Round 1 - Not working</h1>

<p><a href="http://jsbin.com/zopoxaqato/edit?js,console,output" rel="noreferrer">http://jsbin.com/zopoxaqato/edit?js,console,output</a></p>

<pre><code>const input = [
  { value: "a1", colspan: 1, rowspan: 1 },
  { value: "a2", colspan: 1, rowspan: 1 },
  { value: "a3", colspan: 1, rowspan: 3 },

  { value: "b1", colspan: 1, rowspan: 1 },
  { value: "b2", colspan: 1, rowspan: 1 },

  { value: "c1", colspan: 1, rowspan: 1 },
  { value: "c2", colspan: 1, rowspan: 2 },

  { value: "d1", colspan: 1, rowspan: 1 },
  { value: "d3", colspan: 1, rowspan: 1 },

  { value: "e1", colspan: 1, rowspan: 1 },
  { value: "e2", colspan: 2, rowspan: 1 },
];
const width = 3;



const trs = [];
let tds = [];
let rowSpanOffset = 0;

// Loops over entries
input.forEach((cell, index) =&gt; {

  // Stock standard td
  tds.push(`&lt;td colspan="${cell.colspan}" rowspan="${cell.rowspan}"&gt;${cell.value}&lt;/td&gt;`);


  // New row time
  if(index % width === width - 1 || rowSpanOffset &lt; 0) {
    trs.push("&lt;tr&gt;" + tds.join('') + "&lt;/tr&gt;");
    // Reset for next row
    tds = [];    
  }

});


const leTable = "&lt;table class='table'&gt;"+trs.join('')+"&lt;/table&gt;";


$("body").append(leTable);
</code></pre>

<h1>Round 2 - Improved, but assumes input is valid</h1>

<p><a href="http://jsbin.com/solesiyuro/edit?js,output" rel="noreferrer">http://jsbin.com/solesiyuro/edit?js,output</a></p>

<pre><code>const input = [
  { value: "a1", colspan: 1, rowspan: 1 }, // 1
  { value: "a2", colspan: 1, rowspan: 1 }, // 2
  { value: "a3", colspan: 1, rowspan: 3 }, // 3

  { value: "b1", colspan: 1, rowspan: 1 }, // 1
  { value: "b2", colspan: 1, rowspan: 1 }, // 1

  { value: "c1", colspan: 1, rowspan: 1 }, // 1
  { value: "c2", colspan: 1, rowspan: 2 }, // 2

  { value: "d1", colspan: 1, rowspan: 1 }, // 1
  { value: "d3", colspan: 1, rowspan: 1 }, // 1

  { value: "e1", colspan: 1, rowspan: 1 }, // 1
  { value: "e2", colspan: 1, rowspan: 1 }, // 2
];
const width = 3;

const totalCellCount = _.reduce(input, (sum, c) =&gt; sum + c.colspan * c.rowspan, 0);
const grid = _.chunk(_.fill(new Array(totalCellCount), -1), width);

_.each(input, cell =&gt; {
  let start = [-1, -1];

  outerLoop: 
  for(let y = 0; y &lt; grid.length; y++) {
      for(let x = 0; x &lt; width; x++) {
          if(grid[y][x] === -1) {
            start = [x, y];
            break outerLoop;
          }
      }    
  }

  for(let y = 0; y &lt; cell.rowspan; y++) {
      for(let x = 0; x &lt; cell.colspan; x++) {
         grid[start[1] + y][start[0] + x] = null;        
      }    
  }
  grid[start[1]][start[0]] = cell;        

});

let trs = [];
let tds = [];

for(let y = 0; y &lt; grid.length; y++) {
  for(let x = 0; x &lt; grid[y].length; x++) {
    const cell = grid[y][x];
    if(cell) {
      const value = cell.value;
      tds.push('&lt;td colspan="'+cell.colspan+'" rowspan="'+cell.rowspan+'"&gt;'+cell.value+'&lt;/td&gt;');
    }
  }    
  trs.push('&lt;tr&gt;'+tds.join('')+'&lt;/tr&gt;');
  tds = [];
}


$(".table").append(trs.join(''));
</code></pre>

<h2>Edit - Bad input</h2>

<p>An example of bad input would be splitting cells:</p>

<pre><code>const input = [
  { value: "a1", colspan: 1, rowspan: 1 },
  { value: "a2", colspan: 1, rowspan: 2 },
  { value: "a3", colspan: 1, rowspan: 1 },

  { value: "b1", colspan: 3, rowspan: 1 },

];
const width = 3;
</code></pre>