XSLT : Looping from 1 to 60
Asked Answered
H

5

47

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?

Hobbism answered 31/1, 2012 at 8:12 Comment(0)
O
59

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>
Octopod answered 31/1, 2012 at 11:18 Comment(2)
How do you access the current value please? (in the three dots in your first snippet) Tried to use <xsl:variable name="currentCount" select="." /> but with no success.Loudspeaker
Use position()Octopod
L
30

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".

Lightless answered 31/1, 2012 at 14:19 Comment(1)
You are the XSLT-Guru! I had to downgrade my XSLT 2.0 template and your answer was a life saviour!Maggard
B
2

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>
Bilow answered 22/5, 2015 at 13:5 Comment(0)
D
2

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>
Derayne answered 22/3, 2019 at 15:55 Comment(2)
it would be much simpler to use only one param and count backwards.Krystakrystal
@Alejandro You're right. I changed that validation.Derayne
T
0

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

Tieshatieup answered 31/1, 2012 at 9:26 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.