Home > database >  In XSLT, how do I count the number of times each distinct-value of a given attribute value appears i
In XSLT, how do I count the number of times each distinct-value of a given attribute value appears i

Time:11-18

Let's say I have the script of a play in XML:

<speech><speaker name="Bob">BOB</speaker> Hey Jim!</speech>
<speech><speaker name="Jim">JIM</speaker> Hey Bob!</speech>
<speech><speaker name="Bob">BOB</speaker> How's Ethel?</speech>

I want to use XSLT to create a distinct-values list of the speakers, and the number of times each one speaks, in an HTML table, like this:

<table>
            <tr>
               <th>Speaker</th>
               <th>Frequency</th>
            </tr>
            <tr>
               <td>Bob</td>
               <td>2</td>
            </tr>
            <tr>
               <td>Jim</td>
               <td>1</td>
            </tr> </table>

Here's my template rule:

<xsl:template match="/">
        <html>
            <head>Speaker analysis</head>
            <body>
<h1>Table of speech frequency</h1>
            <table>
                <tr><th>Speaker</th><th>Frequency</th></tr>
                <xsl:for-each select="distinct-values(//speaker/@name)">
                    <tr><td><xsl:value-of select="."/></td>
                        <td><xsl:value-of select="count(../speech[speaker[@name='.']])"/></td></tr>
                </xsl:for-each>
            </table>
                    </body></html>
    </xsl:template>

The hitch is in this line:

<td><xsl:value-of select="count(../speech[speaker[@name='.']])"/></td>

What I really want to tell it to do is "for the given distinct-value of a @name identified in this for-loop, count the number of times it appears in the whole document". So I think that I really need to climb back up the ancestor:: axis from that attribute value to count the or elements that contain it. But oXygen gives an error message.

Surely there must be a way to do this?

CodePudding user response:

This ia a case for grouping, not distinct-values:

<xsl:for-each-group select="//speaker" group-by="@name">
    <tr>
        <td>
            <xsl:value-of select="@name"/>
        </td>
        <td>
            <xsl:value-of select="count(current-group())"/>
        </td>
    </tr>
</xsl:for-each-group>

Requires XSLT 2.0 or higher.


P.S. To do it the way you started, you would need to define a key matching speaker by name, then count the sequence returned by the key() function.

Note also that the distinct-values() function returns an independent sequence of values that is not part of the input tree; these values have no ancestors.

  • Related