riderchap
riderchap

Reputation: 687

XSLT Select non duplicate node values

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

Answers (2)

Eir&#237;kr &#218;tlendi
Eir&#237;kr &#218;tlendi

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

Martin Honnen
Martin Honnen

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

Related Questions