How to number a sorted list in XSLT

6.4k views Asked by At

I used xsl:sort in apply-templates to sort the elements and I would like to have them also numbered, but if I try to use xsl:number, then it just gives the original position, not the one after sorting. I guess variables can't be used either because they cannot be changed? So how can I correctly number my list?

2

There are 2 answers

1
Aleksi Yrttiaho On

You can use <xsl:value-of select="position()" /> to get the current position in the loop.

From the xpath spec: The position function returns a number equal to the context position from the expression evaluation context.

XML:

<?xml version="1.0"?>
<?xml-stylesheet type="text/xsl" href="test.xsl"?>
<library>
  <book><name>abc</name></book>
  <book><name>def</name></book>
  <book><name>aaa</name></book>
</library>

XSLT (test.xsl):

<?xml version="1.0" ?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:template match="/">
    <xsl:for-each select="/library/book">
      <xsl:sort select="name" data-type="text" />
      <xsl:value-of select="position()" />: <xsl:value-of select="name" />, 
    </xsl:for-each>
  </xsl:template>
</xsl:stylesheet>
0
Dimitre Novatchev On

From a comment of the OP:

Oh, that worked! Thank you! I was not using the for-each loop, had <xsl:apply-templates select="book"> instead. But with for-each it works

Here are two examples, both using <xsl:apply-templates> -- not <xsl:for-each>:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>

 <xsl:template match="/*">
  <xsl:apply-templates>
   <xsl:sort select=". mod 3"/>
  </xsl:apply-templates>
 </xsl:template>

 <xsl:template match="num">
  <xsl:text>&#xA;</xsl:text>
  <xsl:value-of select="position()"/>: <xsl:text/>
  <xsl:copy-of select="."/>
 </xsl:template>
</xsl:stylesheet>

When this transformation is applied on the following XML document:

<nums>
  <num>01</num>
  <num>02</num>
  <num>03</num>
  <num>04</num>
  <num>05</num>
  <num>06</num>
  <num>07</num>
  <num>08</num>
  <num>09</num>
  <num>10</num>
</nums>

It produces a numbered list of elements, sorted by their remainder when divided by 3:

1: <num>03</num>
2: <num>06</num>
3: <num>09</num>
4: <num>01</num>
5: <num>04</num>
6: <num>07</num>
7: <num>10</num>
8: <num>02</num>
9: <num>05</num>
10: <num>08</num>

The second example is a programming idiom used extensively in 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:strip-space elements="*"/>

 <xsl:template match="/*">
  <numsMax>
   <xsl:apply-templates>
    <xsl:sort data-type="number" order="descending"/>
   </xsl:apply-templates>
  </numsMax>
 </xsl:template>

 <xsl:template match="num">
  <xsl:if test="position()=1">
   <xsl:copy-of select="."/>
  </xsl:if>
 </xsl:template>
</xsl:stylesheet>

When this transformation is applied on the following XML document:

<nums>
  <num>07</num>
  <num>10</num>
  <num>05</num>
  <num>02</num>
  <num>03</num>
  <num>08</num>
  <num>04</num>
  <num>01</num>
  <num>06</num>
  <num>09</num>
</nums>

It produces the maximum of all the numbers, using position()=1 on the sorted node-list:

<numsMax>
   <num>10</num>
</numsMax>