Reputation: 46754
Is it possible in XSLT to sort in alphabetical order, with 5 items as "preferred".
i.e. given
<teams>
<team id="142" name="Scotland" />
<team id="110" name="Liverpool" />
<team id="13" name="Manchester United" />
<team id="123" name="England" />
<team id="84" name="Chelsea" />
<team id="295" name="Wales" />
<team id="49" name="Arsenal" />
<team id="126" name="Northern Ireland" />
<team id="121" name="Republic of Ireland" />
<team id="42" name="Manchester City" />
<team id="298" name="Tottenham Hotspur" />
<team id="299" name="Bolton" />
</teams>
I require the national teams to be sorted first in a certain order, followed by the rest in alphabetical order:
<teams>
<team id="123" name="England" />
<team id="126" name="Northern Ireland" />
<team id="121" name="Republic of Ireland" />
<team id="142" name="Scotland" />
<team id="295" name="Wales" />
<team id="49" name="Arsenal" />
<team id="299" name="Bolton" />
<team id="84" name="Chelsea" />
<team id="110" name="Liverpool" />
<team id="42" name="Manchester City" />
<team id="13" name="Manchester United" />
<team id="298" name="Tottenham Hotspur" />
</teams>
I have been trying, but failing.
Is there a neat way to do this, or do you have to sort the national teams individually, followed by a sort which excludes all the national teams?
Upvotes: 3
Views: 7412
Reputation: 23614
Have you seen xsl:sort element, you can create two or more sorting criteria by combining xsl:apply-templates and xsl:sort
Indeeed, here is a sample where some countries comes first, in a predefined order, then all the others. Note that these are elements, not attributes as above.
<xsl:sort select="not(CountryValue = 'Scotland')"/>
<xsl:sort select="not(CountryValue = 'India')"/>
<xsl:sort select="CountryValue"/>
Sample result:
Country
Scotland
Scotland
Scotland
India
Afghanistan
Afghanistan
Afghanistan
Afghanistan
Afghanistan
Afghanistan
Albania
Albania
Albania
Algeria
Upvotes: 1
Reputation: 176159
Tim C has already given a nice answer, but maybe a simpler way will suffice in your case. You could simply specify two xsl:sort
conditions: The first will sort by preferred/not-preferred items, the second one alphabetically by name:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/teams">
<teams>
<xsl:for-each select="team">
<xsl:sort select="not(@name = 'England' or @name='Northern Ireland'
or @name='Republic of Ireland'
or @name='Scotland' or @name='Wales')"
data-type="number"/>
<xsl:sort select="@name"/>
<team>
<xsl:value-of select="@name"/>
</team>
</xsl:for-each>
</teams>
</xsl:template>
</xsl:stylesheet>
Please note that you have to invert the first condition using not()
. The reason is that the boolean result of your expression is converted to a number (0 is false, 1 is true) and hence the items evaluating to 'false' will be listed first.
Upvotes: 12
Reputation: 338128
You could do this:
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:my="http://tempuri.org"
exclude-result-prefixes="my"
>
<xsl:output method="xml" indent="yes" />
<my:data>
<my:nationalteams>
<my:team id="121" /><!-- Republic of Ireland -->
<my:team id="123" /><!-- England -->
<my:team id="126" /><!-- Northern Ireland -->
<my:team id="142" /><!-- Scotland -->
<my:team id="295" /><!-- Wales -->
</my:nationalteams>
</my:data>
<xsl:template match="teams">
<xsl:copy>
<xsl:variable name="national" select="
document('')/*/my:data/my:nationalteams/my:team
" />
<!-- national teams preferred -->
<xsl:apply-templates select="team[@id = $national/@id]">
<xsl:sort select="@name" />
</xsl:apply-templates>
<!-- other teams after them -->
<xsl:apply-templates select="team[not(@id = $national/@id)]">
<xsl:sort select="@name" />
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
<xsl:template match="team">
<xsl:copy-of select="." />
</xsl:template>
</xsl:stylesheet>
The whole <my:data>
could be moved to a secondary XML/config file, where you can also leave off the "my" namespace.
After that, one line would need a small change:
<xsl:variable name="national" select="
document('config.xml')/data/nationalteams/team
" />
The output of the above is somewhat unsurprising :-)
<teams>
<team id="123" name="England" />
<team id="126" name="Northern Ireland" />
<team id="121" name="Republic of Ireland" />
<team id="142" name="Scotland" />
<team id="295" name="Wales" />
<team id="49" name="Arsenal" />
<team id="299" name="Bolton" />
<team id="84" name="Chelsea" />
<team id="110" name="Liverpool" />
<team id="42" name="Manchester City" />
<team id="13" name="Manchester United" />
<team id="298" name="Tottenham Hotspur" />
</teams>
Upvotes: 6
Reputation: 70598
In your XSLT, are you able to make use of extension functions?
If so, one method is to modify the existing list of nodes in-line, to create a new node set but with an extra 'sortname' attribute on each node. You can then iterate through this new node set, sorting using the new 'sortname' attribute:
<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:exsl="urn:schemas-microsoft-com:xslt">
<xsl:output method="xml" />
<xsl:template match="/teams">
<xsl:variable name="extendedteams">
<xsl:for-each select="team">
<xsl:copy>
<xsl:copy-of select="@*" />
<xsl:attribute name="sortname">
<xsl:choose>
<xsl:when test="@name='England' or @name='Northern Ireland' or @name='Republic of Ireland' or @name='Scotland' or @name='Wales'">1</xsl:when>
<xsl:otherwise>2</xsl:otherwise>
</xsl:choose>
<xsl:value-of select="@name" />
</xsl:attribute>
</xsl:copy>
</xsl:for-each>
</xsl:variable>
<xsl:copy>
<xsl:for-each select="exsl:node-set($extendedteams)/team">
<xsl:sort select="@sortname" />
<xsl:copy>
<xsl:copy-of select="@*[name() != 'sortname']" />
</xsl:copy>
</xsl:for-each>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
In the example above, I prefix a '1' onto any national team, and a '2' on any domestic team, and then sort on this new attribute.
See information on Node Sets to see what XSLT processers support which extensions.
Upvotes: 2