Thursday, March 3, 2011

Set a variable to the smaller of two other variables in XSLT.

When I try to use the code below I get a duplicate variable error because variables are immutable. How do I set the smaller of the two variables ($nextSubPartPos,$nextQuestionStemPos) as my new variable ($nextQuestionPos)?

  <xsl:variable name="nextQuestionPos"/>
  <xsl:choose>
   <xsl:when test="$nextSubPartPos &lt; $nextQuestionStemPos">
    <xsl:variable name="nextQuestionPos" select="$nextSubPartPos"/>
   </xsl:when>
   <xsl:otherwise>
    <xsl:variable name="nextQuestionPos" select="$nextSubPartPos"/>
   </xsl:otherwise>
  </xsl:choose>
From stackoverflow
  • Variables in XSLT are immutable. This has tripped me up so many times.

    joe : Is there any way to get the larger of the two?
    phihag : @joe, yes. Replace min with max in my answer or < and > in the question
    Dimitre Novatchev : Yes, but that's a new question. :) See my answer, it provides more compact and ellegant XPath 1.0 solution and two different XPath 2.0 solutions.
  • Don't close the xsl:variable node in the first line. That is, take the / out of it, then put an </xsl:variable> after </xsl:choose>. Next, change the <xsl:variable> nodes inside the choose to <xsl:value-of> nodes.

    That is, you want to set the value of the variable with the choose. There are two ways to set the value of a variable. One is the select attribute, the other is the inner text of the node.

    <xsl:variable name="nextQuestionPos">
        <xsl:choose>
             <xsl:when test="$nextSubPartPos &lt; $nextQuestionStemPos">
                   <xsl:value-of select="$nextSubPartPos"/>
             </xsl:when>
             <xsl:otherwise>
                   <xsl:value-of select="$nextSubPartPos"/>
             </xsl:otherwise>
        </xsl:choose>
    </xsl:variable>
    
  • Just use the min function:

    <xsl:variable name="a" select="42" />
    <xsl:variable name="b" select="23" />
    <xsl:variable name="x" select="min(($a,$b))" />
    

    In your example, replace the entire code with:

    <xsl:variable name="nextQuestionPos" select="min(($nextSubPartPos,$nextQuestionStemPos))" />
    

    Saxon implements min in the global namespace. Other processors may require a namespace, the correct one (usually denoted fn) is http://www.w3.org/2005/02/xpath-functions .

    nsayer : This solves the immediate problem, but doesn't address the larger question of how to implement complex logic - perhaps logic too complex for XPath - to form a variable's value.
  • A compact XPath 1.0 expression that evaluates to the smaller value is:

        $v1*($v2 >= $v1) + $v2*($v1 > $v2)

    where the $v1 and $v2 variables contain the values to be compared.

    So, an elegant one-liner XSLT 1.0 solution will look like this:

        <xsl:variable name="v3" select="$v1*($v2 >= $v1) +$v2*($v1 > $v2)"/>

    It is easier to define a variable as required in XSLT 2.0:

    One can use either the following (more readable) one-liner:

        if($v2 gt $v1)
                    then $v1
                    else $v2

    Or the more compact:

        min(($v1, $v2))

0 comments:

Post a Comment