Re: [xsl] context-independent counter

Subject: Re: [xsl] context-independent counter
From: Jeni Tennison <mail@xxxxxxxxxxxxxxxx>
Date: Wed, 7 Feb 2001 16:08:35 +0000
Hi Tobias,

> [...] In that case: you don't need a counter. The
>> XSLT processor already counts the nodes in a node set.
>
> I know. But what if the elements that I process are scattered all
> over the doc? Or if I need to start with an element that is the
> fourth? Then I need a context-independent-counter, that doesn't care
> about the position, the level, or any other aspect of the current
> node's context.

Well, if you can use an xsl:for-each loop or iteration using
xsl:apply-templates on a particular node set, then you can always use
the position() of a node within that loop to identify its position
within the node list you're iterating over.

The nodes that you select in an xsl:for-each or xsl:apply-templates
get put into a node list (ordered according to whatever sort you do,
or document order if you don't specify a sort), and their position()
is *always* relevant to that node list, not any other aspect of their
context (such as their level or the number of preceding siblings they
have).

For example, if I'm interested in the 2nd item of the 1st list, the
5th item of the 2nd list and the 13th item of the 20th list, then I
can do:

<xsl:for-each select="list[1]/item[2] | list[2]/item[5] |
                      list[20]/item[13]">
   ...
</xsl:for-each>

and within that xsl:for-each, their positions will be 1, 2 and 3,
despite the diverse places they come from within the document.

If you want to ignore the first of this list, you can do:

<xsl:for-each select="list[1]/item[2] | list[2]/item[5] |
                      list[20]/item[13]">
   <xsl:if test="position() &gt; 1">
      ...
   </xsl:if>
</xsl:for-each>

So in a way, position() *is* that context-independent counter - it
doesn't really depend on where the node is within the document
(although node sets are usually sorted into document order when you
iterate over them).

If you *can't* use an xsl:for-each loop or iteration using
xsl:apply-templates to create a node list, perhaps because the 'next
node' in the sequence that you're generating is dependent on some
property of the 'current node', then you have to use a recursive
solution anyway. And if you're using a recursive solution, then you
can pass the 'counter' as a parameter from template call to template
call:

<xsl:template match="*" mode="list">
   <xsl:param name="counter" select="1" />
   <xsl:apply-templates select="." />
   <xsl:apply-templates select="id(@next-id)">
      <xsl:with-param name="counter" select="$counter + 1" />
   </xsl:apply-templates>
</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