问题
I have an XML like this:
<xml version="1.0" encoding="UTF-8"?>
<countries>
<country>
<name>Latvia</name>
</country>
<country>
<name>USA</name>
</country>
<country>
<name>Australia</name>
</country>
<country>
<name>Indonesia</name>
</country>
<country>
<name>UK</name>
</country>
<country>
<name>India</name>
</country>
<country>
<name>Argentina</name>
</country>
<country>
<name>Chile</name>
</country>
<country>
<name>Singapore</name>
</country>
<country>
<name>New Zeland</name>
</country>
<country>
<name>Kenya</name>
</country>
<country>
<name>Zambia</name>
</country>
<country>
<name>Tunisia</name>
</country>
</countries>
Now I want to create alphabetical index of countries in 3 columns. Each column will contain the number of alpha as one third of the starting alphabets present and its corresponding countries.
Last column can have the rest of them if number of starting alphabets present happens to be not divisible by 3.
For example, here we have country names starting with L, C, U, A, I, S, N, K, Z and T.
After arranging: A C I K L N S T U Z
Now, my index will have:
Column1: A, C and I countries
Column2: K, L and N countries
Column3: S, T, U and Z countries
Hence the desired output is:
<countries>
<column1>
<A>
<country>
<name>Argentina</name>
</country>
<country>
<name>Australia</name>
</country>
</A>
<C>
<country>
<name>Chile</name>
</country>
</C>
<I>
<country>
<name>India</name>
</country>
<country>
<name>Indonesia</name>
</country>
</I>
</column1>
<column2>
<K>
<country>
<name>Kenya</name>
</country>
</K>
<L>
<country>
<name>Latvia</name>
</country>
</L>
<N>
<country>
<name>New Zeland</name>
</country>
</N>
</column2>
<column3>
<S>
<country>
<name>Singapore</name>
</country>
</S>
<T>
<country>
<name>Tunisia</name>
</country>
</T>
<U>
<country>
<name>UK</name>
</country>
<country>
<name>USA</name>
</country>
</U>
<Z>
<country>
<name>Zambia</name>
</country>
</Z>
</column3>
</countries>
Please help. I am using xslt 2.0.
回答1:
I came up with
<?xml version="1.0" encoding="UTF-8" ?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:output indent="yes"/>
<xsl:param name="size" select="3"/>
<xsl:template match="countries">
<xsl:copy>
<xsl:variable name="groups">
<xsl:for-each-group select="country" group-by="substring(name, 1, 1)">
<xsl:sort select="current-grouping-key()"/>
<xsl:element name="{current-grouping-key()}">
<xsl:copy-of select="current-group()"/>
</xsl:element>
</xsl:for-each-group>
</xsl:variable>
<xsl:for-each select="$groups/*[position() mod $size eq 1]">
<xsl:if test="position() le $size">
<xsl:variable name="pos" select="position()"/>
<xsl:element name="column{position()}">
<xsl:copy-of select="., following-sibling::*[if ($pos eq $size) then true() else (position() lt $size)]"/>
</xsl:element>
</xsl:if>
</xsl:for-each>
</xsl:copy>
</xsl:template>
</xsl:transform>
回答2:
I would consider doing this in two passes, first sort and group by letter, giving a sequence of A
- Z
elements, then partition this sequence into columns.
<xsl:variable name="groups" as="element()*">
<xsl:for-each-group select="/countries/country" group-by="substring(name, 1, 1)">
<!-- sort groups alphabetically -->
<xsl:sort select="current-grouping-key()" />
<xsl:element name="{current-grouping-key()}">
<xsl:perform-sort select="current-group()">
<!-- sort names within each group -->
<xsl:sort select="name" />
</xsl:perform-sort>
</xsl:element>
</xsl:for-each-group>
</xsl:variable>
<xsl:variable name="numPerCol" select="count($groups) div 3" />
<column1>
<xsl:sequence select="$groups[position() le $numPerCol]"/>
</column1>
<column2>
<xsl:sequence select="$groups[position() gt $numPerCol and position() le (2*$numPerCol)]" />
</column2>
<column3>
<xsl:sequence select="$groups[position() gt (2*$numPerCol)]" />
</column3>
来源:https://stackoverflow.com/questions/30579085/creating-alphabetical-index-with-fixed-number-of-columns-in-xslt-2-0