Re: [xsl] Finding the first among equals using XPath

Subject: Re: [xsl] Finding the first among equals using XPath
From: "G. Ken Holman" <gkholman@xxxxxxxxxxxxxxxxxxxx>
Date: Thu, 22 Jun 2006 12:02:27 -0400
At 2006-06-22 16:45 +0100, Peter Riocreux wrote:
I am sure this is a hoary old one, but I could not work out the right
runes to achieve it, nor the right keywords to search for to find an
answer

Well said.


I a have a for-each loop over the W elements. Inside the for-each I
want to select one X with each value of Y that exists within that W.

(data copied below)


So for the above example, for //W[1] I want to get the first and third
or second and third X elements, for //W[2] I want all three X elements
and for //W[3] I want any one of the X elements, but only one of them.

I hope that bit is clear.
..
I want a node-set so I can do a for-each on that, so I would prefer to
find a select specifier based on clever use of axes and predicates.

Two answers are below ... both over a node set ... but the first is the variable-based method which allows one to do grouping directly over subtrees, but requires an <xsl:if> in the loop. The second is using the Muenchian method and gives a pure node set but the subtree distinction achieved through the use of the parent's generated identifier may make those responsible for maintaining it difficult to understand what is going on.

I am stuck with using XSL 1.0

That isn't a stigma to bear. I'm still being asked to teach XSLT 1.0.


I hope the two approaches below are helpful.

. . . . . . . . . Ken

t:\ftemp>type peter.xml
<V>
 <W>
  <Z/>
      <X Y='0'/>
   <X Y='0'/>
    <X Y='1'/>
  <Z/>
  </W>
 <W>
  <Z/>
     <Z/>
      <X Y='0'/>
    <X Y='1'/>
     <X Y='2'/>
     </W>
  <W>
    <X Y='2'/>
     <X Y='2'/>
      <X Y='2'/>
  <Z/>
     <Z/>
         </W>
</V>

t:\ftemp>type peter.xsl
<?xml version="1.0" encoding="US-ASCII"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform";
                version="1.0">

<xsl:output method="text"/>

<xsl:template match="V">
  <xsl:for-each select="W">
    <xsl:value-of select="concat('/V/W[',position(),']')"/>
    <xsl:text>
</xsl:text>
    <xsl:variable name="Xs" select="X"/>
    <xsl:for-each select="$Xs">
      <xsl:if test="generate-id(.)=generate-id($Xs[@Y=current()/@Y][1])">
        <xsl:value-of select="concat('got X/@Y=&quot;',@Y,'&quot;')"/>
        <xsl:text>
</xsl:text>
      </xsl:if>
    </xsl:for-each>
  </xsl:for-each>
</xsl:template>

</xsl:stylesheet>
t:\ftemp>xslt peter.xml peter.xsl con
/V/W[1]
got X/@Y="0"
got X/@Y="1"
/V/W[2]
got X/@Y="0"
got X/@Y="1"
got X/@Y="2"
/V/W[3]
got X/@Y="2"

t:\ftemp>type peter2.xsl
<?xml version="1.0" encoding="US-ASCII"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform";
                version="1.0">

<xsl:output method="text"/>

<xsl:key name="Xs" match="X" use="concat(generate-id(..),' ',@Y)"/>

<xsl:template match="V">
  <xsl:for-each select="W">
    <xsl:value-of select="concat('/V/W[',position(),']')"/>
    <xsl:text>
</xsl:text>
    <xsl:variable name="Xs" select="X"/>
    <xsl:for-each select="$Xs[generate-id(.)=
            generate-id(key('Xs',concat(generate-id(..),' ',@Y))[1])]">
      <xsl:value-of select="concat('got X/@Y=&quot;',@Y,'&quot;')"/>
      <xsl:text>
</xsl:text>
    </xsl:for-each>
  </xsl:for-each>
</xsl:template>

</xsl:stylesheet>
t:\ftemp>xslt peter.xml peter2.xsl con
/V/W[1]
got X/@Y="0"
got X/@Y="1"
/V/W[2]
got X/@Y="0"
got X/@Y="1"
got X/@Y="2"
/V/W[3]
got X/@Y="2"

t:\ftemp>


-- Registration open for UBL training: Montrial, Canada 2006-08-07 Also for XSL-FO/XSLT training: Minneapolis, MN 2006-07-31/08-04 Also for UBL/XML/XSLT/XSL-FO training: Varo,Denmark 06-09-25/10-06 World-wide corporate, govt. & user group UBL, XSL, & XML training. G. Ken Holman mailto:gkholman@xxxxxxxxxxxxxxxxxxxx Crane Softwrights Ltd. http://www.CraneSoftwrights.com/s/ Box 266, Kars, Ontario CANADA K0A-2E0 +1(613)489-0999 (F:-0995) Male Cancer Awareness Aug'05 http://www.CraneSoftwrights.com/s/bc Legal business disclaimers: http://www.CraneSoftwrights.com/legal

Current Thread