Night Walker
Night Walker

Reputation: 21260

for loop in xslt page

I get from my xml some integer and I want to get all numbers between 1 and the gotten number .

I want to insert those numbers as a columns names .

Is it possible to do ?

Thanks .

Upvotes: 1

Views: 1011

Answers (2)

Dimitre Novatchev
Dimitre Novatchev

Reputation: 243459

I. XSLT 2.0 solution:

 <xsl:sequence select="1 to $N"/>

II. XSLT 1.0 DVC solution

The answer by @nd (using primitive recursion) is a good one.

However, if a sequence of numbers is to be generated with sufficiently big length (1000 or more) primitive recursion typically ends abnormally with stack overflow due to too-deep recursion.

The DVC (Divide and Conquer) method can be used to avoid call stack overflow for any practical case:

<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 this transformation is applied on any XML document (not used), it outputs the numbers from one to one million. The maximum dept of the call stack during this transformation is only 19 (log2(N)).


III. Non-recursive XSLT 1.0 solution:

In case the length of the numeric sequence to be generated is not too big (more precisely , doesn't exceed the number of the available nodes in an XML document (such as the XSLT stylesheet itself)), then we can use a non-recursive solution, known as the Piez method:

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

 <xsl:variable name="vDoc" select="document('')"/>
 <xsl:variable name="vNodes" select=
 "$vDoc//node() | $vDoc//@* | $vDoc//namespace::*"/>

 <xsl:template match="/">
     <xsl:for-each select="$vNodes[not(position() > 40)]">
      <xsl:value-of select="position()"/>
      <xsl:text>&#xA;</xsl:text>
     </xsl:for-each>
 </xsl:template>
</xsl:stylesheet>

when this transformation is applied to any XML document (not used) it produces the numbers from 1 to 40:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40

Upvotes: 5

nd.
nd.

Reputation: 8932

A word of advice first: Usually such a question indicates that things should be done differently, in a more XSLT-esque way - e.g. by a template matching a source XML tree and using position() to get the index of the current node.

But if you need to, you can get all integer numbers between 1 and an arbitrary number. However, as you cannot reassign variables in XSLT, the standard way to do that is to use recursion:

<xsl:template name="counter">
  <xsl:param name="current"/>
  <xsl:param name="max"/>
  <xsl:if test=" $max >= $current">
    <!-- do whatever you want to do with the current item here -->
    <th><xsl:value-of select="$current"/></th>

    <!-- increase the counter for the recursive call -->
    <xsl:call-template name="counter">
      <xsl:with-param name="current" select="$current + 1"/>
      <xsl:with-param name="max" select="$max"/>
    </xsl:call-template>
  </xsl:if>
</xsl:template>

Then, call the recursive template with a start value and your maximum number:

<xsl:call-template name="counter">
  <xsl:with-param name="current" select="1"/>
  <xsl:with-param name="max" select="$gotten-number"/>
</xsl:call-template>

Upvotes: 1

Related Questions