Created
August 14, 2020 04:15
-
-
Save Adizbek/4d015898121c25c2334c451ca0ffd189 to your computer and use it in GitHub Desktop.
Tree structure to html table, automatic colspan, rowspan
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class Tree { | |
/** @type String */ | |
val; | |
/** @type Tree[] */ | |
children; | |
/** | |
* @param {String} val | |
* @param {Tree[]} children | |
*/ | |
constructor(val, children = []) { | |
this.val = val; | |
this.children = children; | |
} | |
} | |
class Cell { | |
/** @type String */ | |
val; | |
/** @type number */ | |
row; | |
/** @type number */ | |
col; | |
/** @type number */ | |
rowspan; | |
/** @type number */ | |
colspan; | |
/** | |
* @param {String} val | |
* @param {number} row | |
* @param {number} col | |
* @param {number} rowspan | |
* @param {number} colspan | |
*/ | |
constructor(val, row, col, rowspan, colspan) { | |
this.val = val; | |
this.row = row; | |
this.col = col; | |
this.rowspan = rowspan; | |
this.colspan = colspan; | |
} | |
toTag() { | |
let cs = this.colspan === 1 ? "" : ` colspan='${this.colspan}'`; | |
let rs = this.rowspan === 1 ? "" : ` rowspan='${this.rowspan}'`; | |
return `<td${cs}${rs}>${this.val}</td>`; | |
} | |
} | |
class Table { | |
/** | |
* @param {Tree} t | |
* @return {number} | |
*/ | |
width(t) { | |
if (t.children.length === 0) | |
return 1; | |
let w = 0; | |
for (let child of t.children) | |
w += this.width(child); | |
return w; | |
} | |
/** | |
* @param {number} a | |
* @param {number} b | |
* @return {number} | |
*/ | |
lcm(a, b) { | |
let c = a * b; | |
while (b > 0) { | |
let t = b; | |
b = a % b; | |
a = t; | |
} | |
return c / a; | |
} | |
/** | |
* @param {Tree} t | |
* @return {*} | |
*/ | |
rowsToUse(t) { | |
let childrenRows = t.children.length === 0 ? 0 : 1; | |
for (let child of t.children) | |
childrenRows = this.lcm(childrenRows, this.rowsToUse(child)); | |
return 1 + childrenRows; | |
} | |
/** | |
* @param {Tree} t | |
* @param {number} row | |
* @param {number} col | |
* @param {number} rowsLeft | |
* @return {Cell[]} | |
*/ | |
getCells(t, row, col, rowsLeft) { | |
// Add top-most cell corresponding to the root of the current tree. | |
let rootRows = rowsLeft / this.rowsToUse(t); | |
let cells = []; | |
cells.push(new Cell(t.val, row, col, rootRows, this.width(t))); | |
// Generate cells for subtrees. | |
for (let child of t.children) { | |
cells.push(...this.getCells(child, row + rootRows, col, rowsLeft - rootRows)); | |
col += this.width(child); | |
} | |
return cells; | |
} | |
/** | |
* @param {Cell[]} cells | |
*/ | |
getHtmlTable(cells) { | |
let sortedCells = cells.sort((a, b) => { | |
let pri = a.row - b.row | |
let sec = a.col - b.col | |
return pri !== 0 ? pri : sec; | |
}) | |
let table = '<table border="1"><tbody>' | |
for (let i = 0, row = 0; i < sortedCells.length; row++) { | |
if (row === 0) { | |
i++; | |
continue; | |
} | |
table += '<tr>'; | |
for (; i < sortedCells.length && sortedCells[i].row === row; i++) { | |
table += sortedCells[i].toTag(); | |
} | |
table += '</tr>'; | |
} | |
table += '</tbody></table>' | |
return table | |
} | |
/** | |
* @param {Tree} tree | |
*/ | |
printTableTree(tree) { | |
let cells = this.getCells(tree, 0, 0, this.rowsToUse(tree)) | |
return this.getHtmlTable(cells) | |
} | |
} | |
let myTableTree = new Tree(null, [ | |
new Tree("A"), new Tree("B", [ | |
new Tree("C"), new Tree("D"), new Tree("E") | |
]) | |
]); | |
let table = new Table(); | |
let html = table.printTableTree(myTableTree); | |
let fs = require('fs'); | |
fs.writeFileSync('./index.html', html) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment