Re: [xsl] question about position()

Subject: Re: [xsl] question about position()
From: Jeni Tennison <mail@xxxxxxxxxxxxxxxx>
Date: Thu, 17 May 2001 14:51:59 +0100
Hi Denis,

> If I have understand correctly, function position() should return
> position in current context, and for-each example is OK, but I don't
> understand why is output from example with apply-templates
> different.

The xsl:apply-templates itself doesn't change the context in which
something is evaluated.  When you do:

>         <xsl:template match="/">
>                 <xsl:apply-templates select="r/a">
>                         <xsl:sort data-type="number" select="@at"/>
>                         <xsl:with-param name="p" select="position()"/>
>                 </xsl:apply-templates>
>         </xsl:template>

The call to position() is being evaluated with the context node being
the current node in the template itself, i.e. the *root node*.  It's
just as if you did:

<xsl:template match="/">
   <xsl:variable name="p" select="position()" />
   <xsl:apply-templates select="r/a">
      <xsl:sort data-type="number" select="@at" />
      <xsl:with-param name="p" select="$p" />
   </xsl:apply-templates>
</xsl:template>

On the other hand, xsl:for-each *does* change the current node, to the
node that you're looking at within the current run of the loop.  In
the second version of the template:

>         <xsl:template match="/">
>                 <xsl:for-each select="r/a">
>                         <xsl:sort data-type="number" select="@at"/>
>                         <xsl:apply-templates select=".">
>                                 <xsl:with-param name="p" select="position()"/>
>                         </xsl:apply-templates>
>                 </xsl:for-each>
>         </xsl:template>

The xsl:for-each iterates over each of the a elements.  Within that
xsl:for-each, the current node is an a element.  Evaluating *its*
position() within the current node list gives its position.  Again,
the expressions in the xsl:with-param are just as if it was evaluated
outside the xsl:apply-templates - the xsl:apply-templates doesn't
change the current node, so the above is equivalent to:

<xsl:template match="/">
   <xsl:for-each select="r/a">
      <xsl:sort data-type="number" select="@at" />
      <xsl:variable name="p" select="position()" />
      <xsl:apply-templates select=".">
         <xsl:with-param name="p" select="$p" />
      </xsl:apply-templates>
   </xsl:for-each>
</xsl:template>

The simplest thing to achieve the count that you want is not to bother
with parameters at all... just do:

<xsl:template match="/">
   <xsl:apply-templates select="r/a">
      <xsl:sort data-type="number" select="@at" />
   </xsl:apply-templates>
</xsl:template>

<xsl:template match="a">
   <xsl:value-of select="position()" />,<xsl:text />
</xsl:template>

Or:

<xsl:template match="/">
   <xsl:for-each select="r/a">
      <xsl:sort data-type="number" select="@at" />
      <xsl:value-of select="position()" />,<xsl:text />
   </xsl:for-each>
</xsl:template>

I hope that helps,

Jeni

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



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


Current Thread