xsl-list
[Top] [All Lists]

Re: concatenated key

2003-04-23 04:41:21
DaveP wrote:
Given a node, I'm trying to identify/pull out those nodes in the
document having the same set of identically named child nodes. For
example, given the node Alpha in the following, I'd like to pull
out the "Alpha2" node.

<node name="Alpha">
  <node name="a"/>
  <node name="b"/>
</node>
<node name="roman">
  <node name="i"/>
  <node name="ii"/>
</node>
<node name="Alpha2">
  <node name="a"/>
  <node name="b"/>
</node>

Beating Jeni to it for once :-)
In xslt 2 this is

<xsl:template match="node">
  <xsl:variable name="theseChildren" select="node/@name"/>
  <xsl:if test="some $x in ../node/node/@name satisfies ($x =
$theseChildren)">
      <xsl:variable name="this" select="generate-id()"/>
      (I am <xsl:value-of select="@name"/>)
    <xsl:for-each select="../node">
      <xsl:if test="(node/@name = $theseChildren ) and not(generate-id() =
$this)">
        Match at         <xsl:value-of select="position()"/>  <br />
      </xsl:if>
  </xsl:for-each>
</xsl:if>
</xsl:template>

I don't think that works. The test "node/@name = $theseChildren" tests
whether *any* of the name attributes within the current node is the
same as *any* of the name attributes in $theseChildren (just as it
does in XSLT 1.0). So if you had:

<node name="Alpha">
  <node name="a" />
  ...
  <node name="i" />
</node>
<node name="roman">
  <node name="i" />
  <node name="ii" />
</node>

then the above code would say that "roman" was the same as "Alpha"
because they both contain <node name="i" />.

Also note that you don't need to test generate-id() with XPath 2.0 --
you can use the 'is' and 'isnot' operators. Plus you can use "except"
to create a sequence of nodes that doesn't include a node you know.

I think that the best way to approach this problem in XSLT 2.0 would
be to use the string-join() function to concatenate the values of the
name attributes together and then use a key:

<xsl:key name="nodes" match="node[node]"
         use="string-join(node/@name, ' ')" />

<xsl:template match="node">
  <xsl:variable name="others"
    select="key('nodes', string-join(node/@name, ' ')) except ." />
  <xsl:if test="$others">
    <xsl:value-of select="@name" /> matches...
    <xsl:for-each select="$others">
      ...<xsl:value-of select="@name" />
    </xsl:for-each>
  </xsl:if>
</xsl:template>

I would be tempted to turn the "string-join(node/@name, ' ')"
expression into a function to prevent myself from making a mistake by
using a different separator or something in the two places where it's
used.

Cheers,

Jeni

---
Jeni Tennison
http://www.jenitennison.com/


 XSL-List info and archive:  http://www.mulberrytech.com/xsl/xsl-list



<Prev in Thread] Current Thread [Next in Thread>