views:

590

answers:

3

I'm looking to display a list of same-level node names, without duplicates.

Let's say I have

<a>
    <b>
        <c />
        <d />
        <d />
    </b>
    <b>
        <e />
        <c />
        <f />
    </b>
</a>

I'd want c,d,e,f to be displayed. I've found several solutions to a similar problem, eliminating duplicate siblings from output, but I'm having trouble eliminating duplicate "cousins".

A: 

I would use the XPath preceding-sibling axis, and check for same local name. Untested:

<xsl:template match="c|d|e|f">
    <xsl:if test="local-name(.) != local-name(preceding-sibling::*[1])">
       <xsl:copy-of select="."/>
    </xsl:if>
</xsl:template>

IOW, if an element has the same name as its preceding-sibling, it does not get copied.

Martin v. Löwis
I think you mean `preceding-sibling`. ;-) Also, this won't work for all nodes on the same *level*, only for those within the same parent, *and* only if they are already sorted in the document.
Tomalak
Thanks; I fixed the axis name. I now realize that this doesn't solve the OP's problem, though.
Martin v. Löwis
+1  A: 

One possibility:

<!-- make all element nodes accessible by their nesting level -->
<xsl:key name="kNodesByLevel" match="*" use="count(ancestor-or-self::*)" />

<xsl:template match="/">
  <!-- select all nodes on one particular level -->
  <xsl:variable name="lvl" select="key('kNodesByLevel', 3)" />

  <!-- step through them... -->
  <xsl:for-each select="$lvl">
    <xsl:sort select="name()" /> 
    <xsl:variable name="name" select="name()" />
    <!-- ... and group them by node name -->
    <xsl:if test="generate-id() = generate-id($lvl[name() = $name][1])"> 
      <xsl:copy-of select="." />
    </xsl:if>
  </xsl:for-each>
</xsl:template>

Output for the XML you provided:

<c />
<d />
<e />
<f />
Tomalak
A: 

I have the same problem, but worse. When I get to the "c" element, I want to know whether it has occurred anywhere previously in the document - on any level.

(I am working with some very complex xml, where element "c" can appear on a variety of levels in the tree.)

<a>
<b>
    <g>
        <c />
        <d />
        <d />
    </g>
</b>
<b>
    <e />
    <c />
    <f />
</b>

... So when I get to the second occurrence of "c", how can I generically test for previous occurrences of this element, without knowing what level to check on?

(By the way, I could just write a complex expression that successively checks all the various locations where "c" might previously be found... but I'm trying to find a more elegant solution!)

j