OK, I want to apply a XSL style sheet that counts the previous unique "ROLE" nodes and spits out the following output format of @name the number of unique ROLE nodes prior to the current nodes. I've wasted several hours on what should be an easy thing to implement. I have tried to implement this in several ways including the Muenchian Method, if/with variables (Can't increment a variable), applying templates to templates etc to no avail.
I have the following XML:
<ROLEACTIONINFO>
<ROLE name="TESTER" />
<ROLE name="PARENT1"/>
<ROLE name="PARENT1"/>
<ROLE name="PARENT1"/>
<ROLE name="PARENT2"/>
<ROLE name="PARENT2"/>
<ROLE name="PARENT3"/>
<ROLE name="PARENT4"/>
<ROLE name="TESTROLE"/>
</ROLEACTIONINFO>
OUTPUT EXAMPLE:
TESTER 1
PARENT1 2
PARENT1 2
PARENT1 2
PARENT2 3
PARENT2 3
PARENT3 4
PARENT4 5
TESTROLE 6
Getting the count of the unique preceeding nodes is my problem. Any help would be appreciated
This can be solved pretty easily using XPath. Here's the expression you're looking for: count((.|preceding-sibling::ROLE)[not(@name = preceding-sibling::ROLE/@name)])
This can be broken down to make it more readable, as I've done in the following XSLT 1.0 stylesheet:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<!-- don't copy whitespace -->
<xsl:template match="text()"/>
<xsl:template match="ROLE">
<xsl:variable name="roles-so-far" select=". | preceding-sibling::ROLE"/>
<!-- Only select the first instance of each ROLE name -->
<xsl:variable name="roles-so-far-unique"
select="$roles-so-far[not(@name = preceding-sibling::ROLE/@name)]"/>
<xsl:apply-templates select="@name"/>
<xsl:text> </xsl:text>
<xsl:value-of select="count($roles-so-far-unique)"/>
<xsl:text>
</xsl:text> <!-- linefeed -->
</xsl:template>
</xsl:stylesheet>
Here's an alternative implementation, using the Muenchian method. First, declare a key:
<xsl:key name="roles" match="ROLE" use="@name"/>
Then, replace the definition of $roles-so-far-unique with something like this:
<!-- Among all the ROLEs having one of the names so far,
select only the first one for each name -->
<xsl:variable name="roles-so-far-unique"
select="../ROLE[@name = $roles-so-far/@name]
[generate-id(.) = generate-id(key('roles',@name)[1])]"/>
This code, of course, is more complicated. Unless you have a large data set requiring you to speed up processing using the Muenchian method (even then I would test to make sure it buys you anything), you might as well stick with the simpler version above.
Finally, in XSLT 2.0, it's much easier. Simple replace the $roles-so-far-unique definition with the following:
<!-- Return a list of distinct string values, with duplicates removed -->
<xsl:variable name="roles-so-far-unique"
select="distinct-values($roles-so-far/@name)"/>
I hope this has helped you identify where you went wrong in the various attempts that you mentioned.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With