A map associates one key with one value. What you are actually doing here is creating one single-entry map $m
for every iteration of the for
loop. Your code is exactly equivalent to this:
for $r in $c/point
return if ($r/@NR eq '54321') then avg($r/text()) else ()
If you are only looking up a single key, I recommend you not use a map at all and just do this, which is much simpler and faster:
return avg($c/point[@NR='54321'])
Even if you are looking up multiple keys a naive implementation that uses group by
or distinct-values()
may still be faster than a map if BaseX has a fresh attribute index on the document you are querying.
That said, to use a map you need to build up single new map using map:new
and group by
let $c :=
<around>
<point NR="51151">161</point>
<point NR="31252">82</point>
<point NR="54321">323</point>
<point NR="54321">319</point>
<point NR="54321">327</point>
</around>
let $m := map:new(
for $r in $c/point
let $key := $r/@NR, $value := $r/text()
group by $key
return map {$key := $value}
)
return avg($m('54321'))
You can also do this using function reduction (the fold-
functions) instead of group-by:
let $c :=
<around>
<point NR="51151">161</point>
<point NR="31252">82</point>
<point NR="54321">323</point>
<point NR="54321">319</point>
<point NR="54321">327</point>
</around>
let $reducer := function($points as map(*), $point as item()) {
map:new((
$points,
map { $point/@NR := ($points($point/@NR), $point/text())}
))
}
let $m := fold-left($reducer, map{}, $c/point)
return avg($m('54321'))
Remember that in production code you need to generate the map once and then use your list of key lookups on it to have any hope of seeing a speed benefit from using a map.