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.