For Loops in XSLT2

A colleague asked me the other day about the proper way to do for-loops in XSLT2 or more specifically in XPath2. He knows all about xsl:for-each and xsl:for-each-group iteration over things, and of course recursively calling a template while passing a variable to let you count how many times you’ve done it.

I’ve always found that kind of recursion annoying, and in XSLT2 if you just want to do something a number of times, then it is also unnecessary. XPath2 allows you to do XQuery-like for-loops as part of your path statement. Take this short and stupid XSLT2 stylesheet for example:

 
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    version="2.0">
    <xsl:output indent="yes"/>
        <xsl:param name="start" select="1"/>
        <xsl:param name="end" select="10"/>
	<xsl:variable name="from" select="$start"/>
	<xsl:variable name="to" select="$end"/>
    
    <xsl:template match="/" name="main">
        <foo>
        <xsl:for-each select="
            for $i in $from to $to
            return $i
            ">
            <blort><xsl:value-of select="concat('value is: ', . )"/></blort>
        </xsl:for-each>
        </foo>
    </xsl:template>

</xsl:stylesheet>

Let’s break this simple example down a bit.

First we have some starting stuff:

 
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    version="2.0">
    <xsl:output indent="yes"/>
        <xsl:param name="start" select="1"/>
        <xsl:param name="end" select="10"/>
	<xsl:variable name="from" select="$start"/>
	<xsl:variable name="to" select="$end"/>
<!-- ... -->
</xsl:stylesheet>

All this is doing is starting up the stylesheet, saying that we want the result indented, and saying that there are two parameters ‘start’ and ‘end’ which if they aren’t set should be ‘1’ and ’10’ respectively. I then copy these to global variables ‘from’ and ‘to’ just to make my life easier.

 
    <xsl:template match="/" name="main">
        <foo>
        <xsl:for-each select="
            for $i in $from to $to
            return $i
            ">
            <blort><xsl:value-of select="concat('value is: ', . )"/></blort>
        </xsl:for-each>
        </foo>
    </xsl:template>

The whole template here is fairly simple. It either matches the root node ‘/’ or if called by its name (i.e. with “saxon -it:main for-loops.xsl”). We then output a ‘foo’ root element of our output document. Then we have an xsl:for-each which isn’t really the for-loop itself but does something for each iteration of this loop. Each time we create a new number we put out a ‘blort’ element whose content says what the value is. But in order to create the series which the xsl:for-each is iterating over we have made our select statement be “for $i in $from to $to return $i”. This says for a new variable ‘i’ for each of the things in the range from the ‘from’ variable to the ‘to’ variable give use back the value of the ‘i’ variable. So in our case it will create a series from 1 to 10 for the xsl:for-each to operate on.

Hopefully that is the last time I hear that XSLT can’t do for-loops. I’ve put this here to remind me later when I’ve forgotten.

Posted in XSLT | Leave a comment

Leave a Reply