Iterations with for
The let keyword by itself makes sense if you are dealing with one item from a sequence at any given time, but XPath is ultimately a set-manipulation language, and you need to have some way of dealing with the information as a set. This is the domain of the for keyword.
The principle purpose of for is to assign to a temporary variable successive values in a sequence. For instance, the following code line steps you through the first five (named) letters of the Greek alphabet:
for $grkLetter in ("alpha","beta", "gamma","delta","epsilon")
This code first associates the value "alpha" with the variable $grkLetter to perform some processing, then sets the value to "beta", and so forth until it reaches "epsilon". You could also do this with a previously defined sequence stored in a variable:
let $femaleChars := document('characters.xml')//character[@gender = 'Female'] for $femaleCharacter in $femaleCharacters
Similarly, you can use the XPath to operator to iterate over numbers to do something analogous to the for statement in C++, Java, or Visual Basic. This example iterates over the first ten numbers:
for $index in (1 to 10)
After the discussion about XQuery being a functional language in which you are unable to assign multiple values to a single variable name, the use of the for statement might seem to be a clear violation. However, it isn't. Technically speaking, the restriction says you cannot create two variables with the same name in the same scope. This is somewhat analogous to a set of loops in a language such as JavaScript. This language has the var keyword, which indicates that the variable being defined is unique for this scope. For instance, consider the following JavaScript fragment:
for (var index=0; index!= 1; index++){ write(index+":"); for (var index=0;index!=2;index++){ writeln(index); } }
You have two distinct scopes: the first belonging to the outer for loop, the second to the inner. This example, when run, prints a potentially counterintuitive result:
0:0 0:1 0:2 1:0 1:1 1:2
The outer scope is temporarily suspended when a variable with the same name is defined within the inner scope, as long as the inner variable is defined with the var keyword. This makes it possible to avoid the possibility of namespace collisions, where you end up naming a variable the same way someone else named it in some other piece of code.
In essence, the XQuery for operator acts the same waythe local variable (the variable before the in keyword) is defined within the scope of the internal block, something analogous to
for (var tempVar in mySequence){
in a language like JavaScript. $tempVar is instantiated, populated, used, and then destroyed, at which point a second (or third, or fifth, or whatever) $tempVar is created. Because the variable is never created when it already exists, it cannot violate the tenet of reassignment.
Both for and let can also work with full node trees that can be defined inline. For instance, you can create an XQuery that defines a set of regular expression filters, which can be accessed later:
let $filters := ( <filter name="phone" regex="\(\d{3}\)\d{3}-\d{4}"/>, <filter name="zipcode" regex="\d{5}(-\d{4})?"/>, <filter name="email" regex="\w+@\w+\.\w"/> ) for $filter in $filters return $filter
The output is as follows:
<filter name="phone" regex="\(\d{3}\)\d{3}-\d{4}"/> <filter name="zipcode" regex="\d{5}(-\d{4})?"/> <filter name="email" regex="\w+@\w+\.\w"/>
In this case, the sequence of elements is defined explicitly. Because whitespace is not (generally) significant within XML queries, you can create rich XML trees inline:
let $filters := ( <filter> <name>phone</name> <regex>\(\d{3}\)\d{3}-\d{4}</regex> </filter>, <filter> <name>zipcode</name> <regex>\d{5}(-\d{4})?</regex> </filter>, <filter> <name>email</name> <regex>\w+@\w+\.\w </regex> </filter> ) for $filter in $filters return $filter
This code produces slightly more complex output:
<filter> <name>phone</name> <regex>\(\d{3}\)\d{3}-\d{4}</regex> </filter> <filter> <name>zipcode</name> <regex>\d{5}(-\d{4})?</regex> </filter> <filter> <name>email</name> <regex>\w+@\w+\.\w </regex> </filter>