Transpose as Linear Algebra: What Actually Happens When You Flip a Table
TLDR
Flipping an HTML table matrix ($(i, j) \rightarrow (j, i)$) fails when cells containcolspan or rowspan attributes because DOM nodes visually span multiple spatial coordinates. Swapping span attributes (newRowspan = oldColspan) and tracking placed origins via a visited Set prevents duplicate element insertions during transposition.
| Transposition Stage | Spatial Transformation | Span Value Handling | Unique Insertion Guard |
|---|---|---|---|
| Naive Matrix Swap | Swaps indices $(r, c) \rightarrow (c, r)$ | Leaves colspan/rowspan intact | Duplicates/corrupts spanned cells |
| Visual Grid Transpose | Mapped Coordinate Swap | Swaps rowspan $\leftrightarrow$ colspan | 100% Guarded via visited Set |
Transposing raw DOM nodes breaks layouts because merged spans occupy multiple coordinate slots
In linear algebra, transposing a matrix maps every element at position $(i, j)$ to $(j, i)$. In flat 2D arrays, this is accomplished with nested loops.
HTML tables with merged cells break naive loops. A cell with colspan="3" occupies one DOM node but fills three visual coordinate slots. Transposing the array directly places duplicate DOM nodes across multiple rows or drops spanned positions altogether.
Swapping colspans with rowspans and filtering duplicates via a visited set preserves structural integrity
We resolved merged cell transposition by mapping visual coordinates first, swapping span dimensions, and tracking inserted coordinates:
ORIGINAL MATRIX WITH SPANS (3x3 Grid):
┌───────────┬───────┐
│ 0,0 │ 0,2 │ <-- 0,0 has Colspan=2
├───────┬───┴───────┤
│ 1,0 │ 1,1 │ <-- 1,1 has Rowspan=2
├───────┤ │
│ 2,0 │ │
└───────┴───────────┘
TRANSPOSED MATRIX SWAPPING ROWSPAN/COLSPAN: ┌───────┬───────┬───────┐ │ 0,0 │ 0,1 │ 0,2 │ <-- 0,0 now has Rowspan=2 │ ├───────┼───────┤ │ │ 1,1 │ │ <-- 1,1 now has Colspan=2 ├───────┤ │ │ │ 2,0 └───────┴───────┘ └───────────────────────┘
Here is the transposition logic accounting for spanned cells:
// Transpose visual grid matrix while swapping span parameters
export function transposeTableWithMapper(mapper) {
const transposedGrid = [];
for (let c = 0; c < mapper.maxCols; c++) { transposedGrid[c] = []; for (let r = 0; r < mapper.maxRows; r++) { transposedGrid[c][r] = mapper.grid[r]?.[c] || null; } }
const visitedCoordinates = new Set(); const resultRows = [];
transposedGrid.forEach((row, rowIndex) => { const rowCells = []; row.forEach((cellData, colIndex) => { const coordKey = ${rowIndex},${colIndex}; if (visitedCoordinates.has(coordKey) || !cellData?.isOrigin) return;
// Swap span attributes: original colspan becomes new rowspan const newRowspan = cellData.colspan; const newColspan = cellData.rowspan;
// Mark occupied spanned coordinates as visited for (let r = 0; r < newRowspan; r++) { for (let c = 0; c < newColspan; c++) { visitedCoordinates.add(${rowIndex + r},${colIndex + c}); } }
rowCells.push({ element: cellData.element, newRowspan, newColspan }); }); resultRows.push(rowCells); });
return resultRows; }
Swapping span values ensures horizontal header spans transform into vertical side spans, keeping the table structure intact after matrix inversion.
Rule of thumb: Swapcolspanandrowspanattributes during table transposition to maintain spatial proportions.