Counter in for-each, position() is not useful

xml, xslt

Solution

The `position()` function is context-sensitive - it gives you the position of the current node within the "current node list", i.e. the list of nodes extracted by the `select` expression of the current `for-each` or `apply-templates`. So if you do something like

<xsl:for-each select="product">
  <xsl:if test="@type = 'A'">
    <li><xsl:value-of select="position()"/>: <xsl:value-of select="@name" /></li>
  </xsl:if>
</xsl:for-each>

then you will get `position()` values of 1, 3 and 5 because the `select` selects all five product elements. But if instead you put the `@type` test in the `select` expression:

<xsl:for-each select="product[@type = 'A']">
  <li><xsl:value-of select="position()"/>: <xsl:value-of select="@name" /></li>
</xsl:for-each>

then you will get positions 1, 2 and 3 because the `for-each` is only processing the three product elements whose `@type` is A, rather than all five of them.

In a more complex case where you really do need to process all the `product` elements (e.g. if you're doing something with the type A ones and something different with the type B ones, but need to keep the document order) then you'd need to do a trick with the `preceding-sibling::` axis, e.g.

<xsl:if test="@type = 'A'">
  <xsl:value-of select="count(preceding-sibling::product[@type = 'A']) + 1" />
</xsl:if>

to explicitly count the number of preceding `product` elements with the same `@type` as this one.

Problem

How to use a counter without use position() in XSLT? For example: XML ``` <product type="A" name="pepe"/> <product type="B" name="paco"/> <product type="A" name="Juan"/> <product type="B" name="Edu"/> <product type="A" name="Lauren"/> ``` I want to show all the types "A" in order with their number: ``` 1.pepe 2.Juan 3.Lauren ``` The xsl will be something like that ``` <xsl:for-each select="./products"> <xsl:if test="./products/product/@type="A""> <tr> <xsl:value-of select="position()"/> <xsl:value-of select="./product/@name"/> </tr> </xsl:if> </xsl:for-each> ```

Original source