问题
Here is a scenario where an alphabetical index is created into 3 columns. But, the approach for dividing them into columns has some scope of improvement.
Creating alphabetical index with fixed number of columns in XSLT 2.0
In the above scenario, is there a way if the 3 columns can be made as much equal as possible without breaking a letter group.
回答1:
I would suggest the following approach:
XSLT 2.0
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:param name="columns" select="3"/>
<xsl:template match="/countries">
<xsl:variable name="indexed-countries">
<xsl:for-each select="country">
<xsl:sort select="."/>
<country index="{position()}">
<xsl:copy-of select="@*|*"/>
</country>
</xsl:for-each>
</xsl:variable>
<xsl:variable name="groups">
<xsl:for-each-group select="$indexed-countries/country" group-by="substring(name, 1, 1)">
<group name="{current-grouping-key()}" cumulative-size="{current-group()[last()]/@index}">
<xsl:copy-of select="current-group()"/>
</group>
</xsl:for-each-group>
</xsl:variable>
<xsl:variable name="n" select="count(country)" />
<xsl:variable name="col-limit" select="ceiling($n div $columns)" />
<!-- output -->
<xsl:copy>
<xsl:for-each select="1 to $columns">
<xsl:variable name="low" select="(. - 1) * $col-limit" />
<xsl:variable name="high" select=". * $col-limit" />
<xsl:element name="column{.}">
<xsl:copy-of select="$groups/group[$low lt @cumulative-size and @cumulative-size le $high]"/>
</xsl:element>
</xsl:for-each>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Given your input example, the result will be:
<?xml version="1.0" encoding="UTF-8"?>
<countries>
<column1>
<group name="A" cumulative-size="2">
<country index="1">
<name>Argentina</name>
</country>
<country index="2" x="AU">
<name>Australia</name>
</country>
</group>
<group name="C" cumulative-size="3">
<country index="3">
<name>Chile</name>
</country>
</group>
<group name="I" cumulative-size="5">
<country index="4">
<name>India</name>
</country>
<country index="5">
<name>Indonesia</name>
</country>
</group>
</column1>
<column2>
<group name="K" cumulative-size="6">
<country index="6">
<name>Kenya</name>
</country>
</group>
<group name="L" cumulative-size="7">
<country index="7">
<name>Latvia</name>
</country>
</group>
<group name="N" cumulative-size="8">
<country index="8">
<name>New Zeland</name>
</country>
</group>
<group name="S" cumulative-size="9">
<country index="9">
<name>Singapore</name>
</country>
</group>
<group name="T" cumulative-size="10">
<country index="10">
<name>Tunisia</name>
</country>
</group>
</column2>
<column3>
<group name="U" cumulative-size="12">
<country index="11">
<name>UK</name>
</country>
<country index="12">
<name>USA</name>
</country>
</group>
<group name="Z" cumulative-size="13">
<country index="13">
<name>Zambia</name>
</country>
</group>
</column3>
</countries>
Of course, in the real implementation, you will want use xsl-apply-templates
instead of xsl:copy-of
here:
<xsl:copy-of select="$groups/group[$low lt @cumulative-size and @cumulative-size le $high]"/>
to remove the "scaffolding" and format the output to your liking.
来源:https://stackoverflow.com/questions/30614777/adjust-an-alphabetical-index-from-xml-grouping-to-make-columns-equal