[xsl] Running through a subset of elements based on index

Subject: [xsl] Running through a subset of elements based on index
From: Adam Nielsen <adam.nielsen@xxxxxxxxx>
Date: Thu, 10 Apr 2008 19:10:21 +1000
Hi all,

I'm a bit stuck trying to figure out how to write an XSL condition to get me the data I want.

I have a list of years as elements, for example:

  <year>2005</year>
  <year>2005</year>
  <year>2006</year>
  <year>2007</year>
  <year>2007</year>
  <year>2008</year>
  <year>2008</year>
  <year>2008</year>
  etc.

and I want to get a list of five years, for display in a table (it has to be split so the first five years are in one table, the next five years are in a second table, and so on.) Each year should only appear once, even if it appears multiple times in the source data.

At the moment this all works fine, but in order to find the list of years I take the first year and add four to find the last year - so if the first year is 2001, the last year will be 2001+4 == 2005.

The problem with this is if one or more years are missing from the list, in order to get five years to display I'll have to consider *more* than five calendar years, for example to display the years 2005, 2006, 2009, 2010, 2011, I'll need to display up to the first year + 6, and this will of course depend on how many years are missing from the list.

What I really need is some way of saying "display year X, and the four years chronologically following it, not counting omitted years." (Bearing in mind each year can appear in the source file multiple times, in any order.)

I'm a bit stuck as I think I'll have to use position() somehow to get the position five elements ahead of the current year (once I've removed duplicates and sorted numerically), but I'm not quite sure how to do that. (Or to clarify, I can remove the duplicates and sort numerically, but I don't know how to run through just five elements.)

At the moment the (simplified) relevant parts of my code look like this:

<xsl:key name="funding-years" match="//year" use="." />

<xsl:variable name="aAllYears" select="//year[count(. | key('funding-years', .)[1]) = 1 and (. &gt;= $firstYear) and (. &lt;= $firstYear+4)]"/>

<xsl:for-each select="$aAllYears">
  <xsl:sort select="." />
  Year: <xsl:value-of select="."/><br/>
</xsl:for-each>

This works, but only when there are no years omitted from the sequence. It all sits in a function which gets called once for every five years (and this next code snippet *does* work, even if years are omitted):

<xsl:for-each select="//year[count(. | key('funding-years', .)[1]) = 1]">
  <xsl:sort select="." />
  <xsl:variable name="strYear" select="." />
  <xsl:if test="(position()-1) mod 5 = 0">
    <xsl:call-template name="listFunding">
      <xsl:with-param name="firstYear" select="$strYear"/>
    </xsl:call-template>
  </xsl:if>
</xsl:for-each>

Does anyone have any hints how I could make the first code snippet work even if one or more years are entirely omitted?

Many thanks,
Adam.

Current Thread