user1369070
user1369070

Reputation: 11

about xslt count()

I want to simply have a textual output that shows the song title and the number of the weeks that ranked #1. But, I don't know how to use count in xslt. Can anyone help me find out what's wrong with my code? The codes I have right now doesn't have any output.

<?xml version='1.0'?>
<xsl:stylesheet 
    version="2.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
>
<xsl:output encoding="UTF-8" omit-xml-declaration="yes" />  
<xsl:template match="/">
    <xsl:for-each select="popular-recordings-dataset/recording">
    <xsl:sort select="count(//rank[. = '1'])" order="descending"/>
    <xsl:value-of select="concat(title, ' / ', count(//rank[. = '1']),'&#xA;')" />
    </xsl:for-each>    
</xsl:template>   
</xsl:stylesheet>

Upvotes: 1

Views: 205

Answers (1)

Dimitre Novatchev
Dimitre Novatchev

Reputation: 243579

This transformation:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output method="text"/>

 <xsl:template match="/*">
  <xsl:apply-templates select="recording">
    <xsl:sort select="count(week/rank[. = 1])"
         data-type="number" order="descending"/>
  </xsl:apply-templates>
 </xsl:template>

 <xsl:template match="recording">
  <xsl:value-of select=
  "concat(title, ' / ', count(week/rank[. = 1]), '&#xA;')"/>
 </xsl:template>
</xsl:stylesheet>

when applied on the following XML document (none provided in the question !!!):

<popular-recordings-dataset>
  <recording>
   <title>Song1</title>
   <week no="23">
    <rank>1</rank>
   </week>
   <week no="24">
    <rank>2</rank>
   </week>
  </recording>
  <recording>
   <title>Song2</title>
   <week no="22">
    <rank>1</rank>
   </week>
   <week no="24">
    <rank>1</rank>
   </week>
   <week no="25">
    <rank>1</rank>
   </week>
  </recording>
  <recording>
   <title>Song3</title>
   <week no="21">
    <rank>1</rank>
   </week>
   <week no="26">
    <rank>1</rank>
   </week>
  </recording>
</popular-recordings-dataset>

produces the wanted, correct result:

Song2 / 3
Song3 / 2
Song1 / 1

Explanation: The problem with your code is in using this expression:

count(//rank[. = '1']) 

The argument to count() is an absolute XPath expression (starts with /), therefore it selects all rank elements in the document with value equal to 1. This means that this expression doesn't depend on and vary with the current rank element and remains the same for all rank elements. Sorting on a constant doesn't change the initial ordering of the node-set -- as you have witnessed.

Solution:

Change:

count(//rank[. = '1']) 

to:

count(.//rank[. = '1']) 

But better, if the structure of the XML document is known in advance, then try to avoid using the // pseudo-operator, which can be very inefficient.

Upvotes: 1

Related Questions