@   Test position

My original problem was to simply display a list of elements in a table with alternate coloring (for even and odd rows).
The obvious answer was "use the position of the row in the list".
While this works fine on unordered lists, you get very interesting effects when you sort the list.

The reason is that the position returned by the at $pos in a for statement always returns the position of an element related to the initial list, even if you are sorting the list with order by.

This is actually the same behavior of an XSLT transformation that uses <xsl:sort/> (<xsl:for-each/> or <xsl:apply-templates>).

But the main advantage of XQuery over XSLT in this case is that with XSLT you are trained to avoid the <xsl:for-each/> statement and prefer the use of <xsl:apply-templates> when possible.

In XQuery the for statement is, instead, one of the main constructs of the language (the F in FLOWR). This means that we can abuse the statement, but most importantly it means that its execution is highly optimized (and also the language allows for more expressivenes than XSLT).

In this case we can achieve the desired effect by executing 2 for cycles instead of one.

The first is used to select the elements to list and sort them in a temporary structure. The second is used to scan the ordered list sequentially and print it.

And, almost by magic, this time if we query the position of a sorted element we will get the expected sequentially increasing index.

The following example illustrates the problem and its solution. The variable $unordered contains an unordered list of words. The variable $ordered is generated by using the first for cycle to sort the unordered list. The snippets of code show the result of accessing the list with or without the order by modifier.

 

 

Variable definition


define variable $unordered 
  { "alpha", "zebra", "charlie", "fox", "bravo", "x-ray", "delta", "lima" }
define variable $ordered 
  { for $e in $unordered order by $e ascending return $e }

Lists

$unordered: ("alpha", "zebra", "charlie", "fox", "bravo", "x-ray", "delta", "lima")

  for $e at $i in $l
    return concat(string($i), ":", $e)

  1:alpha 2:zebra 3:charlie 4:fox 5:bravo 6:x-ray 7:delta 8:lima
$ordered: ("alpha", "bravo", "charlie", "delta", "fox", "lima", "x-ray", "zebra")

  for $e at $i in $l
    return concat(string($i), ":", $e)

  1:alpha 2:bravo 3:charlie 4:delta 5:fox 6:lima 7:x-ray 8:zebra

Print ordered list


  for $e at $i in $l
    order by $e ascending
    return concat(string($i), ":", $e)

  1:alpha 5:bravo 3:charlie 7:delta 4:fox 8:lima 6:x-ray 2:zebra

Powered by MarkLogic Server Standard edition ver. 3.2-1 Send comments or suggestions to raff@aromatic.org