Scott
Scott

Reputation: 1310

XSLT position() - exlude elements that fail test condition

lets say I have the following xml file:

<jobs>
  <job>
    <PositionTitle>Painter</PositionTitle>
    <InternalOrExternal>External</InternalOrExternal>
  </job>
  <job>
    <PositionTitle>Plumber</PositionTitle>
    <InternalOrExternal>Internal</InternalOrExternal>
  </job>
  <job>
    <PositionTitle>Chemist</PositionTitle>
    <InternalOrExternal>Internal</InternalOrExternal>
  </job>
  <job>
    <PositionTitle>Teacher</PositionTitle>
    <InternalOrExternal>External</InternalOrExternal>
  </job>
</jobs> 

.

I process it with the following xslt to show only the external jobs:

<?xml version="1.0" encoding="UTF-8"?>
 <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:content="http://purl.org/rss/1.0/modules/content/" >
  <xsl:output method="html"/>
  <xsl:template match="/">

        <xsl:for-each select="jobs/job">

            <xsl:if test="InternalOrExternal = 'External'"><!-- Only show external jobs -->
              <xsl:value-of select="PositionTitle"/> - <xsl:value-of select="position()"/><br />
            </xsl:if>

        </xsl:for-each>

 </xsl:template>
 </xsl:stylesheet>

This results as:

Painter - 1
Teacher - 4

I assume the position() function returns the actual position in the xml file and doesn't take into account any if statements that may exclude some jobs. In this case the Internal jobs are excluded but their position is still counted.

The result that I want is:

Painter - 1
Teacher - 2

Is their any way I can get the position() function to only count what I display?

I've tried this with no luck:

<xsl:value-of select="position(jobs/job[InternalOrExternal='External'])"/>

Upvotes: 1

Views: 1008

Answers (2)

Daniel Haley
Daniel Haley

Reputation: 52858

In addition to Ian Roberts' great answer: If you had to have the context be job (for whatever reason), you could also use xsl:number instead of position().

Example:

<xsl:value-of select="PositionTitle"/> - <xsl:number count="job[InternalOrExternal='External']"/><br />

Upvotes: 0

Ian Roberts
Ian Roberts

Reputation: 122374

position() gives you the position of the current node within the "current node list", i.e. whatever list of nodes were selected by the most recent for-each or apply-templates. You can think of this informally as the current iteration number of the for-each1. By saying

<xsl:for-each select="jobs/job">

the current node list is all the job elements, so you get position 1 for the first one and position 4 for the fourth one. If you remove the xsl:if and instead move the filtering into a predicate on the for-each selector:

<xsl:for-each select="jobs/job[InternalOrExternal='External']">
  <xsl:value-of select="PositionTitle"/> - <xsl:value-of select="position()"/><br />
</xsl:for-each>

then the current node list only includes the "External" job elements, and you'll get the position() values you require.


1 Since XSLT instructions don't have side effects (e.g. there are no updateable variables), the XSLT processor doesn't necessarily have to implement for-each internally using a sequential loop. It could choose to process different nodes in parallel or out of order, as long as it assembles the output in such a way that it looks the same as a sequential loop would produce.

Upvotes: 4

Related Questions