I'm trying to go from this kind of input:
<col title="one">
<cell>a</cell> <cell>b</cell> <cell>c</cell> <cell>d</cell>
</col>
<col title="two">
<cell>e</cell> <cell>f</cell> <cell>g</cell>
</col>
... to this HTML output with XSLT:
<table>
<tr> <th>one</th> <th>two</th> </tr>
<tr> <td>a</td> <td>e</td> </tr>
<tr> <td>b</td> <td>f</td> </tr>
<tr> <td>c</td> <td>g</td> </tr>
<tr> <td>d</td> </tr>
</table>
In other words I want to perform a matrix transposition. I couldn't find a simple way to do that, there probably isn't, I guess; how about a complicated one? While searching on Google I found hints that a way to solve this was through recursion. Any idea appreciated.
One possibility is to find the <col>
with the most cells and then iterate over them in a nested loop. This guarantees the generation of a structurally valid HTML table.
<!-- this variable stores the unique ID of the longest <col> -->
<xsl:variable name="vMaxColId">
<xsl:for-each select="/root/col">
<xsl:sort select="count(cell)" data-type="number" order="descending" />
<xsl:if test="position() = 1">
<xsl:value-of select="generate-id()" />
</xsl:if>
</xsl:for-each>
</xsl:variable>
<!-- and this selects the children of that <col> for later iteration -->
<xsl:variable name="vIter" select="
/root/col[generate-id() = $vMaxColId]/cell
" />
<xsl:template match="root">
<xsl:variable name="columns" select="col" />
<table>
<!-- output the <th>s -->
<tr>
<xsl:apply-templates select="$columns/@title" />
</tr>
<!-- make as many <tr>s as there are <cell>s in the longest <col> -->
<xsl:for-each select="$vIter">
<xsl:variable name="pos" select="position()" />
<tr>
<!-- make as many <td>s as there are <col>s -->
<xsl:for-each select="$columns">
<td>
<xsl:value-of select="cell[position() = $pos]" />
</td>
</xsl:for-each>
</tr>
</xsl:for-each>
</table>
</xsl:template>
<xsl:template match="col/@title">
<th>
<xsl:value-of select="." />
</th>
</xsl:template>
Applied to
<root>
<col title="one">
<cell>a</cell> <cell>b</cell> <cell>c</cell> <cell>d</cell>
</col>
<col title="two">
<cell>e</cell> <cell>f</cell> <cell>g</cell>
</col>
</root>
this produces:
<table>
<tr>
<th>one</th> <th>two</th>
</tr>
<tr>
<td>a</td> <td>e</td>
</tr>
<tr>
<td>b</td> <td>f</td>
</tr>
<tr>
<td>c</td> <td>g</td>
</tr>
<tr>
<td>d</td> <td></td>
</tr>
</table>
From Marrow:
<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" indent="yes"/>
<xsl:template match="input">
<table border="1">
<xsl:apply-templates select="col[1]/cell"/>
</table>
</xsl:template>
<xsl:template match="cell">
<xsl:variable name="curr-pos" select="position()"/>
<tr>
<td>
<xsl:copy-of select="node()|../following-sibling::col/cell[$curr-pos]/node()"/>
</td>
</tr>
</xsl:template>
</xsl:stylesheet>
I put input tags around your xml to make it closer match an example I found.
(getting closer).
BTW: you can test by adding this as your 2nd line to your xml:
<?xml-stylesheet type="text/xsl" href="NonLinear.xslt"?>