Two-column tables using XSLT
xmltable, xslt
Solution
Explanation
Your table must have two `<td>` per row (if you want two columns). Your XSLT does generate only one.
Solution is to interate over one half of the list and generate two `<td>` per iteration.
So first I would define a size of the table. Example:
<xsl:param name="size" select="count(catalog/cd)"/>
Then iterate over only a half of it (`$size div 2`). The number must be rounded if the input list can contain a non-even number of elements: `ceiling($size div 2)` (Rounding up to catch last element)
<xsl:for-each select="catalog/cd[ceiling($size div 2) >= position()]">
In each iteration, first render an element itself:
<td><xsl:value-of select="title"/></td>
Then render an appropriate element from the second half of the table (offset is the number defined before: `ceiling($size div 2)` Half size of the table)
<td><xsl:value-of select="following::cd[ceiling($size div 2)]/title"/></td>
You can wrap element rendering in a separate template to avoid code repeating.
Working example
Check this transformation example with W3C XSL TryIt (http://www.w3schools.com/xsl/tryxslt.asp?xmlfile=cdcatalog&xsltfile=cdcatalog):
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<xsl:param name="size" select="count(catalog/cd)"/>
<html>
<body>
<h2>My CD Collection</h2>
<table border="1">
<tr bgcolor="#9acd32">
<th>Title</th>
<th>Title</th>
</tr>
<xsl:for-each select="catalog/cd[ceiling($size div 2) >= position()]">
<tr>
<td><xsl:value-of select="title"/></td>
<td><xsl:value-of select="following::cd[ceiling($size div 2)]/title"/></td>
</tr>
</xsl:for-each>
</table>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
It splits CD-catalog (given in example link above) in two columns.
Problem
Here is a fraction of the XML data I am processing ``` <?xml version="1.0" encoding="utf-16"?> <ScorecardSummary> <DivisionSummary> <DivisionName> <string> SYSTEM</string> </DivisionName> <ScorecardSummaryByDivision> <ScorecardSummaryByKPI> <Header> <string>Committed Time of Arrival</string> <string>Goal</string> <string>1D</string> <string>7D</string> <string>QTD</string> <string>YTD</string> <string>YTD Event Cars</string> </Header> <Data> <ScorecardContract> <TypeName>System</TypeName> <Goal>68</Goal> <GoalWarning>64.6</GoalWarning> <TotalCountYear>1234</TotalCountYear> <Value1D>79</Value1D> <Value7D>79.2</Value7D> <ValueQTD>79.1</ValueQTD> <ValueYTD>73.3</ValueYTD> </ScorecardContract> <ScorecardContract> <TypeName>AG</TypeName> <Goal>68</Goal> <GoalWarning>64.6</GoalWarning> <TotalCountYear>1111</TotalCountYear> <Value1D>80.9</Value1D> <Value7D>78.7</Value7D> <ValueQTD>78.4</ValueQTD> <ValueYTD>69.7</ValueYTD> </ScorecardContract> ``` This is a small part of the XSL that produces the tables: ``` <xsl:template match="ScorecardSummary/DivisionSummary/DivisionName"> <h1> <xsl:value-of select="current()/string"/> </h1> </xsl:template> <xsl:template match="ScorecardSummaryByDivision"> <xsl:apply-templates select="current()/ScorecardSummaryByKPI"/> </xsl:template> <xsl:template match="ScorecardSummaryByKPI"> <table border="1" cellspacing="0" cellpadding="5"> <tr> <xsl:choose> <xsl:when test="count(preceding-sibling::ScorecardSummaryByKPI) mod 6 < 4"> <td> <table border="1" cellspacing="0" cellpadding="5"> <xsl:apply-templates select="Header"/> <xsl:apply-templates select="Data"/> </table> </td> </xsl:when> <xsl:otherwise> <td> <table border="1" cellspacing="0" cellpadding="5"> <xsl:apply-templates select="Header"/> <xsl:apply-templates select="Data"/> </table> </td> </xsl:otherwise> </xsl:choose> </tr> </table> </xsl:template> ``` The XSL produces 6 tables repeatedly like this: ``` 1 2 3 4 5 6 1 2 3 4 5 6 ``` But I want to order them like this: ``` 1 4 2 5 3 6 1 4 2 5 3 6 ``` and so on. I tried using this check, but it doesn't work. ``` count(preceding-sibling::ScorecardSummaryByKPI) mod 6 < 4 ``` Can anyone help?