This question must be edited in order for anyone to understand what the problem really is. The comment by Tomalak reveals that the OP "wants lists of items in an alphabetically ordered grid. One list for each letter. Four letters horizontally, as much as it takes vertically"
The following transformation:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ext="http://exslt.org/common"
extension-element-prefixes="ext"
>
<xsl:variable name="vDoc" select="/"/>
<xsl:variable name="vNumCols" select="4"/>
<xsl:variable name="vLower"
select="'abcdefghijklmnopqrstuvwxyz'"
/>
<xsl:variable name="vUpper"
select="'ABCDEFGHIJKLMNOPQRSTUVWXYZ'"
/>
<xsl:key name="rows-by-FirstLetter" match="Row"
use="translate(substring(@Title,1,1),
'abcdefghijklmnopqrstuvwxyz',
'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" />
<xsl:variable name="vrtfStartLetters">
<xsl:for-each select=
"/*/*/Row
[count(.
|
key('rows-by-FirstLetter',
translate(substring(@Title,1,1),
$vLower,
$vUpper)
)[1]
)
= 1
]">
<startLetter>
<xsl:value-of select=
"translate(substring(@Title,1,1),
$vLower,
$vUpper)"/>
</startLetter>
</xsl:for-each>
</xsl:variable>
<xsl:variable name="vStartLetters" select=
"ext:node-set($vrtfStartLetters)"/>
<xsl:template match="Rows">
<table>
<xsl:apply-templates select=
"$vStartLetters/*[position() mod $vNumCols = 1]">
<xsl:with-param name="pDoc" select="$vDoc"/>
<xsl:with-param name="pNumCols" select="$vNumCols"/>
</xsl:apply-templates>
</table>
</xsl:template>
<xsl:template match="startLetter">
<xsl:param name="pDoc"/>
<xsl:param name="pNumCols" select="10"/>
<tr>
<xsl:apply-templates mode="copy" select=
". | following-sibling::*
[not(position() >= $pNumCols)]">
<xsl:with-param name="pDoc" select="$pDoc"/>
<xsl:sort/>
</xsl:apply-templates>
</tr>
</xsl:template>
<xsl:template match="startLetter" mode="copy">
<xsl:param name="pDoc"/>
<xsl:variable name="pThis" select="."/>
<td>
<xsl:value-of select="."/>
<br />
<table>
<xsl:for-each select="$pDoc">
<xsl:for-each select="key('rows-by-FirstLetter', $pThis)">
<tr><td><xsl:value-of select="@Title"/></td></tr>
</xsl:for-each>
</xsl:for-each>
</table>
</td>
</xsl:template>
</xsl:stylesheet>
when applied on this XML document:
<dsQueryResponse>
<Rows>
<Row Title="Agenda" />
<Row Title="Accrual" />
<Row Title="Ads" />
<Row Title="Averages" />
<Row Title="Bindings" />
<Row Title="Budget" />
<Row Title="Cars" />
<Row Title="Categories" />
<Row Title="Costs" />
<Row Title="Policy" />
<Row Title="Politics" />
<Row Title="Reevaluations" />
<Row Title="Report" />
</Rows>
</dsQueryResponse>
produces the wanted result:
<table>
<tr>
<td>A
<br/>
<table>
<tr>
<td>Agenda</td>
</tr>
<tr>
<td>Accrual</td>
</tr>
<tr>
<td>Ads</td>
</tr>
<tr>
<td>Averages</td>
</tr>
</table>
</td>
<td>B
<br/>
<table>
<tr>
<td>Bindings</td>
</tr>
<tr>
<td>Budget</td>
</tr>
</table>
</td>
<td>C
<br/>
<table>
<tr>
<td>Cars</td>
</tr>
<tr>
<td>Categories</td>
</tr>
<tr>
<td>Costs</td>
</tr>
</table>
</td>
<td>P
<br/>
<table>
<tr>
<td>Policy</td>
</tr>
<tr>
<td>Politics</td>
</tr>
</table>
</td>
</tr>
<tr>
<td>R
<br/>
<table>
<tr>
<td>Reevaluations</td>
</tr>
<tr>
<td>Report</td>
</tr>
</table>
</td>
</tr>
</table>
Do note three things:
We are using the (exslt) ext:node-set() extension function to convert an intermediate result from RTF (Result-Tree Fragment) to a temporary tree.
The <xsl:for-each select="$pDoc">
necessary to make the original XML document the current XML document again, so that the key() function will use the index made for this document and not for the temporary tree.
Each start letter that must start a new row of (4) starting letters is processed in a special template, in which the <tr>
is produced. Then this and the remaining (3) starting letters in the row are processed within the body of the <tr>
in "copy" mode, just creating a <td>
each.
Here we have covered and demonstrated a few advanced XSLT techniques:
- Using the
mod
operator for grouping by quantity.
- Using the key() function for a different document than the current one.
- Modes
- Converting an RTF into a temporary tree
Enjoy :)