Reputation: 687
I am trying to get the non duplicate node values from a list. I have tried many suggestions but nothing quite worked for me. This is my source XML
<Records>
<Record>
<Files>
<File>
<Name>A</Name>
</File>
<File>
<Name>B</Name>
</File>
<File>
<Name>B</Name>
</File>
</Files>
</Record>
<Record>
<Files>
<File>
<Name>A</Name>
</File>
<File>
<Name>B</Name>
</File>
<File>
<Name>C</Name>
</File>
<File>
<Name>C</Name>
</File>
</Files>
</Record>
</Records>
Output I am looking for should looks like a text like this
A,B | A,B,C
First comma separated set is from the first record and second set (after "|") is from the second record. (Delimiter placement, spaces etc... are not my issue, it is removal of duplicates)
Code I have now looks like this
<xsl:key name="NameId" match="Name" use="." />
<xsl:for-each select="Records/Record">
<xsl:call-template name="doeach_record"/>
<xsl:text>|</xsl:text>
</xsl:for-each>
<xsl:template name="doeach_record">
<xsl:for-each select="Files/File">
<xsl:if test="generate-id(Name) = generate-id(key('NameId', Name)[1])">
<xsl:value-of select="Name"/>
</xsl:if>
<xsl:text>,</xsl:text>
</xsl:for-each>
</xsl:template>
It remove duplicates from the first record, for the second record it is not selecting Name values that are found in first record. I am getting output something like this
A,B | ,,C
Upvotes: 1
Views: 822
Reputation: 1180
As Martin notes, in XSLT 2.0, distinct-values()
is your friend.
Here's a slightly different approach for XSLT 2.0. This includes sorting, in case your input XML isn't already alphabetized.
<xsl:output method="text"/>
<xsl:template match="/">
<xsl:for-each select="/Records/Record">
<xsl:for-each select="distinct-values(Files/File/Name)">
<xsl:sort/>
<xsl:value-of select="."/>
<xsl:if test="position() != last()">,</xsl:if>
</xsl:for-each>
<xsl:if test="position() != last()"> | </xsl:if>
</xsl:for-each>
</xsl:template>
Upvotes: 1
Reputation: 167581
There is XSLT 2.0 since 2007 with various implementations like Saxon 9, XmlPrime, Altova where you could use <xsl:template match="Record"><xsl:value-of select="distinct-values(Files/File/Name)" separator=","/></xsl:template>
. If you really are restricted to XSLT 1.0 then you need to define a key that takes the generated-id of the ancestor Record
together with the Name
e.g. <xsl:key name="unique" match="File/Name" use="concat(generate-id(ancestor::Record), '|', .)"/>
, then you can use the usual XSLT 1.0 key based Muenchian grouping/distinct value approach.
Upvotes: 1