Ianthe
Ianthe

Reputation: 5709

XSLT : Looping from 1 to 60

What is the best way to loop in XSLT from 1 to 60? I research in net, there are some templates to do this, is there any other way for example like a built-in function?

Upvotes: 48

Views: 86545

Answers (5)

David Castro
David Castro

Reputation: 1975

The basic example for V1.0 using recursion would it be like this:

<xsl:template match="/">
<Root>
      <!-- Main Call to MyTemplate -->
     <xsl:call-template name="MyTemplate" />
</Root>
</xsl:template>

<xsl:template name="MyTemplate">
  <xsl:param name="index" select="1" />
  <xsl:param name="maxValue" select="60" />

  <MyCodeHere>
     <xsl:value-of select="$index"/>
  </MyCodeHere>

  <!-- &lt; represents "<" for html entities -->
  <xsl:if test="$index &lt; $maxValue">
    <xsl:call-template name="MyTemplate">
        <xsl:with-param name="index" select="$index + 1" />
        <xsl:with-param name="total" select="$maxValue" />
    </xsl:call-template>
  </xsl:if>
</xsl:template>

Upvotes: 3

Aart den Braber
Aart den Braber

Reputation: 862

Very simple check inside the foreach-loop

<xsl:if test="$maxItems > position()">
    do something
</xsl:if>

Based on Dimitre Novatchev's answer.

Example:

<xsl:variable name="maxItems" select="10" />
<xsl:variable name="sequence" select="any-sequence"/>

<xsl:for-each select="$sequence">

    <!-- Maybe sort first -->
    <xsl:sort select="@sort-by" order="descending" />

    <!-- where the magic happens -->
    <xsl:if test="$maxItems > position()">
        do something
    </xsl:if>
</xsl:for-each>

Upvotes: 2

Dimitre Novatchev
Dimitre Novatchev

Reputation: 243529

The problem with simple recursion when processing long sequences is that often the space for the call stack becomes insufficient and the processing ends due to stack overflow. This typically happens with sequence length >= 1000.

A general technique to avoid this (implementable with any XSLT processor, even if it doesn't recognize tail-recursion) is DVC (Divide and Conquer) style recursion.

Here is an example of a transformation that successfully prints the numbers from 1 to 1000000 (1M):

<xsl:stylesheet version="1.0"
     xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
     <xsl:output method="text"/>

     <xsl:template match="/">
      <xsl:call-template name="displayNumbers">
        <xsl:with-param name="pStart" select="1"/>
        <xsl:with-param name="pEnd" select="1000000"/>
      </xsl:call-template>
     </xsl:template>

     <xsl:template name="displayNumbers">
      <xsl:param name="pStart"/>
      <xsl:param name="pEnd"/>

      <xsl:if test="not($pStart > $pEnd)">
       <xsl:choose>
        <xsl:when test="$pStart = $pEnd">
          <xsl:value-of select="$pStart"/>
          <xsl:text>&#xA;</xsl:text>
        </xsl:when>
        <xsl:otherwise>
          <xsl:variable name="vMid" select=
           "floor(($pStart + $pEnd) div 2)"/>
          <xsl:call-template name="displayNumbers">
           <xsl:with-param name="pStart" select="$pStart"/>
           <xsl:with-param name="pEnd" select="$vMid"/>
          </xsl:call-template>
          <xsl:call-template name="displayNumbers">
           <xsl:with-param name="pStart" select="$vMid+1"/>
           <xsl:with-param name="pEnd" select="$pEnd"/>
          </xsl:call-template>
        </xsl:otherwise>
       </xsl:choose>
      </xsl:if>
     </xsl:template>
</xsl:stylesheet>

When applied on any XML document (not used) this transformation produces the wanted result -- all the numbers from 1 to 1000000.

You can use/adapt this transformation for any task that needs to "do something N times".

Upvotes: 31

Michael Kay
Michael Kay

Reputation: 163458

In XSLT 2.0,

<xsl:for-each select="1 to 60">...</xsl:for-each>

But I guess that you must be using XSLT 1.0, otherwise you wouldn't be asking.

In XSLT 1.0 you should use recursion: a template that calls itself with a counter that's incremented on each call, and the recursion terminates when the required value is reached.

Alternatively there's a workaround in XSLT 1.0: provided your source document contains at least 60 nodes, you can do

<xsl:for-each select="(//node())[60 >= position()]">...</xsl:for-each>

Upvotes: 60

Rubens Farias
Rubens Farias

Reputation: 57966

XSLT works based on templates and you'll need a template do run that loop.

You'll need to build a template receiving start and end values and, inside it, make a recursive call computing with start + 1. When $start equals $end, you do return your template, without another call.

In practice: http://www.ibm.com/developerworks/xml/library/x-tiploop/index.html

Upvotes: 0

Related Questions