Reputation: 89
I have the following xml:
<form>
<bibl>
<biblScope>1</biblScope>
<biblScope>a</biblScope>
</bibl>
<bibl>
<biblScope>2</biblScope>
<biblScope>b</biblScope>
</bibl>
<bibl>
<biblScope>38</biblScope>
<biblScope>c</biblScope>
</bibl>
</form>
Using this XQuery
for $bibl in form/bibl
return
<div>
{
for $biblScope in $bibl/biblScope/text()
return
$biblScope
}
{if ($bibl[position()] ne $bibl[last()]) then ',' else '@'}
</div>
The <biblScope>
contents are printed one after another. After each bibl
element, I would like to add a separator (comma / ",") except for the last one, but with the code above, all I get is
<div>
1a
@
</div>
<div>
2b
@
</div>
<div>
38c
@
</div>
and this is definitely wrong, because what I would like to have is
<div>1a,</div>
<div>2b,</div>
<div>38c@</div>
(add a comma after every bibl
element content; the last one is supposed to be followed by @
instead of a comma.)
Having tried different things for some time now, I could need some help. What is the proper way to do this?
Upvotes: 2
Views: 819
Reputation: 38682
The problem is that position()
and last()
work on the current context, which is not set by flwor expressions. If you want to use similar semantics, use the at $position
syntax to get a position counter, and define $last
as the number of results:
let $last := count(form/bibl)
for $bibl at $position in form/bibl
return
<div>
{
for $biblScope in $bibl/biblScope/text()
return
$biblScope
}
{if ($position ne $last) then ',' else '@'}
</div>
If you're able to use XQuery 3.0, the application operator !
might be of use here. Replacing the unnecessary loops by axis steps and element constructors in those, you can rely on the positional predicates, as the context is set to to <bibl/>
elements:
form/bibl ! <div>
{
biblScope/text(),
if (position() ne last()) then ',' else '@'
}
</div>
Upvotes: 4
Reputation: 163352
First note that this:
for $biblScope in $bibl/biblScope/text()
return
$biblScope
can be replaced by this:
$bibl/biblScope/text()
(This bit of verbosity is a surprisingly common mistake. It suggests you're thinking procedurally - processing items one at a time, rather than processing sets.)
Then this:
<div>
{$bibl/biblScope/text()}
{if ($bibl[position()] ne $bibl[last()]) then ',' else '@'}
</div>
should be replaced by this:
<div>{string-join($bibl/biblScope, ',')}@</div>
Note that I've also got rid of the unnecessary (and arguably incorrect) use of /text()
, which is another XQuery anti-pattern.
Upvotes: 4