I want to filter an array of maps based on a value for one of the keys. The problems seems to be that the second parameter of array:filter()
is a function that accepts only a single item parameter: array:filter($array as array(*), $function as function(item()*) as xs:boolean) as array(*)
according to the XPath and XQuery Operators 3.1 specs.
But when I do array:filter($routingTable, function ($i) {$i?input ne $wid})
, with $wid
being defined somewhere before this call, this variable is not being passed into the function and the comparison misses the crucial entries. (Checking the value of $wid
with debugging output right before the call to array:filter
confirms it has the correct value. Checking $i?input
inside the anonymous functions confirms this value, too. But checking $wid
inside the anonymous function makes it appear it is empty.)
So I thought maybe I need to pass the value to compare against to the filtering function as a second parameter, but when I do array:filter($routingTable, function ($i, $wid) {etc...
, I get a java.lang.ArrayIndexOutOfBoundsException
error. I assume this is due to the excess argument of the function. How should I go about this?
For what it's worth, my XQuery processor is eXist-db (6.1.0) and here is more complete code (the call that's at issue (I assume) is the second line of the last function):
(: for a sequence of nodes, I build maps and put them into an array. that is my routing table that is then posted :)
declare function my:createRoutes($wid as xs:string) {
let $index := doc($config:index-root || "/" || $wid || ".xml")/my:index
let $routingTable := array{fn:for-each($index//my:node, function($k) {my:buildRoutingInfoNode($wid, $k)} )}
return my:postRoutingTable($routingTable)
};
(: helper function to create a map from a node :)
declare function my:buildRoutingInfoNode($wid as xs:string, $item as element(my:node)) {
map { "input" : concat($wid, ":", $item/@citeID/string()), "outputs" : array { ( $item/@crumb/string(), 'yes' ) } }
};
(: here the routing table is posted. However, if the entries are already present in the "live" table, I need to clean them from there first :)
declare function my:postRoutingTable($routes as array(*)) as xs:integer {
if (array:size($routes) = 0) then
0
else
let $testmap := $routes?1 (: okay, that's a bit cheap: I just check the first of the new entries. :)
let $src := $testmap?input
let $dest := $testmap?outputs
return if (not(my:isInRoutingTable($src, $dest))) then
... post via http request ...
else (: At least one key is already present, need to clean routing table for $wid first :)
let $wid := substring-before($src, ":")
let $cleanStatus := my:cleanRoutingTable($wid)
return if ($cleanStatus ge 0) then
my:postRoutingTable($routes) (: retry ... :)
else
-1 (: cleaning failed ... :)
};
(: remove all entries about the $wid (so that I can add them again) :)
declare function my:cleanRoutingTable($wid as xs:string) as xs:integer {
let $routingTable := my:getRoutingTable() (: get "live" table :)
let $cleanedRT := array:filter($routingTable, function ($i) {
substring($i?input, 1, 5) ne $wid
}) (: remove all entries concerning the to-be-posted $wid :)
let $deleteStatus := my:deleteRoutingTable() (: drop the complete live table :)
return if (array:size($cleanedRT) > 0) then (: if after removing entries, anything is left of the original "live" table, :)
my:postRoutingTable($cleanedRT) (: then re-post this "cleaned" table :)
else -1
};
On the face of it this looks like an eXist-db bug. The variable $wid
is part of the closure of the anonymous function and its value should be accessible; your code looks fine -- though without a complete repro (source document and expected results) I haven't tested it elsewhere.