Read XSLT 2.0 and XPath 2.0 Programmer's Reference, 4th Edition Online
Authors: Michael Kay
A common mistake is to forget that
for
expressions don't set the context node. The following example is wrong (it's not an error, but it doesn't do what the writer probably intended):
(:wrong:) sum(for $i in item return @price * @qty)
The correct way of writing this is:
(:correct:) sum(for $i in item return $i/@price * $i/@qty)
Generally speaking, there is usually something amiss if the range variable is not used in the
return
expression. However, there are exceptions to this rule. For example, it's quite reasonable to write:
string-join(for $i in 1 to $n return “-”, “”)
which returns a string containing
$n
hyphens.
It's also often (but not invariably) a sign of trouble if the value of the return expression depends on the context item. But it's not actually an error: the context item inside the return expression is exactly the same as the context item for the
for
expression as a whole. So it's legal to write an expression such as:
chapter/(for $i in 1 to 10 return section[$i])
which returns the first 10 sections of each chapter.
Combining Multiple Sequences
The
for
expression allows multiple input sequences to be defined, each with its own range variable. For example, you can write:
for $c in //customer,
$o in $c/orders,
$ol in $o/line
return $ol/cost
The simplest way to think about this is as a nested loop. You can regard the
,
as a shorthand for writing the keywords
return for
, so the above expression is equivalent to:
for $c in //customer
return
for $o in $c/orders
return
for $ol in $o/line
return $ol/cost
Note that each of the range variables can be referenced in the expression that defines the input sequence for the next range variable.
In the example above, each iteration is rather like a step in a path expression; it selects nodes starting from the node selected in the containing loop. But it doesn't have to be this way. For example, you could equally write an expression such as:
for $c in doc(‘customers.xml’)//customer,
$p in doc(‘products.xml’)//product [$c/orders/product-code = $p/code]
return $c/line/cost