patternjavascriptMinor
Generating an HTML table with colspan and rowspan from a one-dimensional array
Viewed 0 times
colspanwitharraydimensionalgeneratingoneandfromhtmltable
Problem
I will be given the following input:
And from such, I need to construct an HTML table, taking into account colspan and rowspans. The resulting table would look like:
This works, but I'm not sure if it's the best approach or if it could be tackled better:
JS Bin
I'm using ES6/ES2015, although I could use template strings. Ignore them for now because it seems to freak out jsbin.
The data set is reasonably small, so
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;And from such, I need to construct an HTML table, taking into account colspan and rowspans. The resulting table would look like:
*----+----+----*
| a1 | a2 | a3 |
+----+----+ |
| b1 | b2 | |
+----+----+ |
| c1 | c2 | |
+----+ +----+
| d1 | | d2 |
+----+----+----+
| e1 | e2 |
*----+---------*This works, but I'm not sure if it's the best approach or if it could be tackled better:
// Define the total possible cell count
// This poorly assumes no gaps
const totalCellCount = reduce(input, (sum, c) => sum + (c.colSpan * c.rowSpan), 0);
// Fill a 2d array with placeholders so the below section
// can find the next available "spot". Chunk is being
// used to create the 2d array
const grid = chunk(fill(new Array(totalCellCount), -1), columnCount);
each(input, cell => {
// A default "not found" state.
let start = { x: -1, y: -1 };
// Search for the next available spot working from
// left to right, top to bottom
outerLoop: for(let y = 0; y '+value+'');
}
}
// End of this row, assemble and push into
// the tr array
trs.push(''+tds.join('')+'');
// Reset the td array for the next row
tds = [];
}
// Simple join and attach to screen
$(".table").append(trs.join(''));JS Bin
I'm using ES6/ES2015, although I could use template strings. Ignore them for now because it seems to freak out jsbin.
The data set is reasonably small, so
Solution
I really wanted to find a purely functional solution to this problem, but I wasn't able to. Nevertheless, your basic idea is pretty simple, and can be made even shorter and more declarative:
Note: I used one ramda library function just to break the array into subarrays. Lodash has a similar one, but I prefer ramda.
Here's a working bin of the final solution:
http://jsbin.com/nimuca/2/edit?js,output
// Cell calculations as pure data. "cells" is the variable containing
// all cells, which we are filling in
var width = 3,
numCells = input.reduce((m,x) => m += x.rowspan + x.colspan -1, 0),
cells = new Array(numCells).fill(false),
dummy = { dummy: true},
addAcross = (i,n) => R.times(j => cells[i+j+1] = dummy, n),
addDown = (i,n) => R.times(j => cells[i+(j+1)*width] = dummy, n),
nextIndex = () => cells.findIndex(x => !x);
// Everything happens here. We fill in "cells" with the real
// cells and dummy cells for the span cells
input.forEach(x => {
var i = nextIndex();
cells[i] = x;
addAcross(i, x.colspan-1);
addDown(i, x.rowspan-1);
})
// HTML View Helpers
// Once we have the data, just split the array into subarrays
// for each row, and filter out the dummy cells. In this format
// constructing the html itself is trivial
var rows = R.splitEvery(width, cells).map(r => r.filter(x => !x.dummy)),
asTd = c => `${c.value}`,
asTr = row => `${row.map(asTd).join('')}`,
asTable = rows => `${rows.map(asTr).join('')}`;
document.querySelector('body').innerHTML = asTable(rows);Note: I used one ramda library function just to break the array into subarrays. Lodash has a similar one, but I prefer ramda.
Here's a working bin of the final solution:
http://jsbin.com/nimuca/2/edit?js,output
Code Snippets
// Cell calculations as pure data. "cells" is the variable containing
// all cells, which we are filling in
var width = 3,
numCells = input.reduce((m,x) => m += x.rowspan + x.colspan -1, 0),
cells = new Array(numCells).fill(false),
dummy = { dummy: true},
addAcross = (i,n) => R.times(j => cells[i+j+1] = dummy, n),
addDown = (i,n) => R.times(j => cells[i+(j+1)*width] = dummy, n),
nextIndex = () => cells.findIndex(x => !x);
// Everything happens here. We fill in "cells" with the real
// cells and dummy cells for the span cells
input.forEach(x => {
var i = nextIndex();
cells[i] = x;
addAcross(i, x.colspan-1);
addDown(i, x.rowspan-1);
})
// HTML View Helpers
// Once we have the data, just split the array into subarrays
// for each row, and filter out the dummy cells. In this format
// constructing the html itself is trivial
var rows = R.splitEvery(width, cells).map(r => r.filter(x => !x.dummy)),
asTd = c => `<td rowspan=${c.rowspan} colspan=${c.colspan}>${c.value}</td>`,
asTr = row => `<tr>${row.map(asTd).join('')}</tr>`,
asTable = rows => `<table border=1>${rows.map(asTr).join('')}</table>`;
document.querySelector('body').innerHTML = asTable(rows);Context
StackExchange Code Review Q#128248, answer score: 2
Revisions (0)
No revisions yet.