JohnX
JohnX

Reputation: 13

How to list each distinct element and count all occurances of each element in XSLT/XPATH?

I would like to reduce a list to distinct elements but count the number of each occurence of that element:

<list>
  <entry>A</entry>
  <entry>B</entry>
  <entry>A</entry>
  <entry>A</entry>
  <entry>B</entry>
</list>

aimed result:

A - 3 [occurences]

B - 2 [occurences]

I have tried to use

<xsl:value-of select="count()" />

but it either counts all occurences of the "entry" element (5) or the number different "entry" elements (2, being A and B).

My latest try was to combine two for-each loops (never done before), but that doesn't work either:

<xsl:variable name="occurences">
<xsl:for-each select="//entry[not(.=preceding::entry)]">
<xsl:value-of select="count()" />
</xsl:for-each>
</xsl:variable>

<xsl:template match="/">
<xsl:for-each select="//entry">
<p>
     <xsl:value-of select="." /> - <xsl:value-of select="$occurences" />
</p>
</xsl:for-each>
</xsl:template>

[An XPath function was called with the wrong number of arguments.]

I'd be grateful for any hint...

Thank you! John

Upvotes: 1

Views: 1433

Answers (1)

Tim C
Tim C

Reputation: 70648

If you can indeed use XSLT 2.0, you can use the xsl:for-each-group here, but you need to select entry elements, not the single list element, as it is entry elements you wish to group.

Try this XSLT

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

  <xsl:template match="list">
    <table> 
      <xsl:for-each-group select="entry" group-by="."> 
         <tr> 
            <td><xsl:value-of select="."/></td> 
            <td><xsl:value-of select="count(current-group())"/></td> 
         </tr> 
       </xsl:for-each-group> 
     </table>
  </xsl:template>
</xsl:stylesheet>

Alternatively, here is how you would do it with distinct-values

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="xml" indent="yes" />
  <xsl:key name="entry" match="entry" use="." />
  <xsl:variable name="root" select="/" />

  <xsl:template match="list">

    <table> 
      <xsl:for-each select="distinct-values(entry)"> 
         <tr> 
            <td><xsl:value-of select="."/></td> 
            <td><xsl:value-of select="count(key('entry', ., $root))"/></td> 
         </tr> 
       </xsl:for-each> 
     </table>
  </xsl:template>
</xsl:stylesheet>

Note the use of the third parameter in the key function, as within the loop you are no longer in the context of the main document.

Upvotes: 1

Related Questions