I. XSLT 2.0 Solution :
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" exclude-result-prefixes="xs"> <xsl:output omit-xml-declaration="yes" indent="yes"/> <xsl:param name="pColLength" as="xs:integer" select="10"/> <xsl:template match="/*"> <names> <xsl:for-each-group select="name" group-by="substring(.,1,1)"> <xsl:sort select="current-grouping-key()"/> <xsl:for-each-group select="current-group()" group-by="(position()-1) idiv $pColLength"> <column> <xsl:copy-of select="current-group()"/> </column> </xsl:for-each-group> </xsl:for-each-group> </names> </xsl:template> </xsl:stylesheet>
when applied to this XML document (since there was no such question in the question):
<names> <name>T A</name> <name>T B</name> <name>T C</name> <name>T D</name> <name>T E</name> <name>T F</name> <name>T G</name> <name>T H</name> <name>T I</name> <name>T J</name> <name>T K</name> <name>T L</name> <name>A A</name> <name>A B</name> <name>A C</name> <name>A D</name> <name>A E</name> <name>A F</name> <name>A G</name> <name>A H</name> <name>A I</name> <name>A J</name> <name>A K</name> <name>A L</name> <name>X A</name> <name>X B</name> <name>X C</name> <name>X D</name> <name>X E</name> <name>X F</name> <name>X G</name> <name>X H</name> <name>X I</name> <name>X J</name> <name>X K</name> <name>X L</name> <name>R A</name> <name>R B</name> <name>R C</name> <name>R D</name> <name>R E</name> <name>R F</name> <name>R G</name> <name>R H</name> <name>R I</name> <name>R J</name> <name>R K</name> <name>R L</name> </names>
creates the desired names sorted by the first letter and placed in columns of 10 elements :
<names> <column> <name>A A</name> <name>A B</name> <name>A C</name> <name>A D</name> <name>A E</name> <name>A F</name> <name>A G</name> <name>A H</name> <name>A I</name> <name>A J</name> </column> <column> <name>A K</name> <name>A L</name> </column> <column> <name>R A</name> <name>R B</name> <name>R C</name> <name>R D</name> <name>R E</name> <name>R F</name> <name>R G</name> <name>R H</name> <name>R I</name> <name>R J</name> </column> <column> <name>R K</name> <name>R L</name> </column> <column> <name>T A</name> <name>T B</name> <name>T C</name> <name>T D</name> <name>T E</name> <name>T F</name> <name>T G</name> <name>T H</name> <name>T I</name> <name>T J</name> </column> <column> <name>T K</name> <name>T L</name> </column> <column> <name>X A</name> <name>X B</name> <name>X C</name> <name>X D</name> <name>X E</name> <name>X F</name> <name>X G</name> <name>X H</name> <name>X I</name> <name>X J</name> </column> <column> <name>X K</name> <name>X L</name> </column> </names>
Explanation
Nested xsl:for-each-group - are first grouped by the start character, then for each such group, a defined and sorted group - by the number of the column in which its elements should be located.
Using the standard XSLT 2.0 functions current-grouping-key() and current-group() .
Solution II.XSLT 1.0 :
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output omit-xml-declaration="yes" indent="yes"/> <xsl:param name="pColLength" select="10"/> <xsl:key name="kStarting" match="name" use="substring(.,1,1)"/> <xsl:template match="/*"> <names> <xsl:for-each select= "name [generate-id() = generate-id(key('kStarting', substring(.,1,1))[1]) ] "> <xsl:sort select="substring(.,1,1)"/> <xsl:variable name="vgroupNames" select= "key('kStarting', substring(.,1,1))"/> <xsl:apply-templates select="$vgroupNames[1]"> <xsl:with-param name="pGroup" select="$vgroupNames"/> <xsl:with-param name="pGroupLength" select= "count($vgroupNames)"/> </xsl:apply-templates> </xsl:for-each> </names> </xsl:template> <xsl:template match="name"> <xsl:param name="pGroup"/> <xsl:param name="pGroupLength"/> <xsl:param name="pInd" select="1"/> <xsl:if test="not($pInd > $pGroupLength)"> <column> <xsl:copy-of select= "$pGroup [position() >= $pInd and not(position() > $pInd + $pColLength -1) ]"/> </column> <xsl:apply-templates select= "$pGroup[position() = $pInd + $pColLength]"> <xsl:with-param name="pGroup" select="$pGroup"/> <xsl:with-param name="pGroupLength" select="$pGroupLength"/> <xsl:with-param name="pInd" select="$pInd + $pColLength"/> </xsl:apply-templates> </xsl:if> </xsl:template> </xsl:stylesheet>
if applied to the same XML document (as indicated above), the same desired result is created - the names are sorted by starting the first letter and placed in columns of 10 elements each :
<names> <column> <name>A A</name> <name>A B</name> <name>A C</name> <name>A D</name> <name>A E</name> <name>A F</name> <name>A G</name> <name>A H</name> <name>A I</name> <name>A J</name> </column> <column> <name>A K</name> <name>A L</name> </column> <column> <name>R A</name> <name>R B</name> <name>R C</name> <name>R D</name> <name>R E</name> <name>R F</name> <name>R G</name> <name>R H</name> <name>R I</name> <name>R J</name> </column> <column> <name>R K</name> <name>R L</name> </column> <column> <name>T A</name> <name>T B</name> <name>T C</name> <name>T D</name> <name>T E</name> <name>T F</name> <name>T G</name> <name>T H</name> <name>T I</name> <name>T J</name> </column> <column> <name>T K</name> <name>T L</name> </column> <column> <name>X A</name> <name>X B</name> <name>X C</name> <name>X D</name> <name>X E</name> <name>X F</name> <name>X G</name> <name>X H</name> <name>X I</name> <name>X J</name> </column> <column> <name>X K</name> <name>X L</name> </column> </names>
Explanation
Using the Muenchian grouping method , as well as sorting, we get (in sorted order) each group of name elements, consisting of all names starting with the same character.
Each group of name elements obtained above is processed by applying patterns to its first name element . The entire group, its length and the index of the name element in the group (default = 1) are passed as parameters.
The template corresponding to the name element is guaranteed to apply only to the original element within the column . It creates a new column element and copies in it all the name elements for this column (starting with the index $pInd and ending with the index $pInd+$pColLength -1 ). There is no requirement that these elements be siblings (and they were not) t) If each name requires not just copying, but also additional processing, this can be done by replacing the <xsl:copy-of> statement with:
-
<xsl:apply-templates mode="process" select= "$pGroup [position() >= $pInd and not(position() > $pInd + $pColLength -1) ]"/>
Dimitre novatchev
source share