[xsl] Subtle (or not?) Map Processing Bug in XQuery

Subject: [xsl] Subtle (or not?) Map Processing Bug in XQuery
From: "Eliot Kimber ekimber@xxxxxxxxxxxx" <xsl-list-service@xxxxxxxxxxxxxxxxxxxxxx>
Date: Fri, 7 Jan 2022 16:18:57 -0000
Ibm starting to use XQuery again after being away from it for a few years
and also trying to re-learn how to operate on maps efficiently as my current
XQuery use is largely data analysis and reporting over large bodies of DITA
content, so therebs lots of reference management and resolution to be done,
lots of fiddly bits to keep track of.



I ran into a coding bug that threw me for a minute and thought Ibd mention
it here just in case it throws anyone else in the future (including future
me).



In my XQuery script Ibm building a map from all the image files in a
directory tree (using BaseXbs file extensions):



let $files := file:list($rootDir, true(), b*.pngb)

let $imageMap := map:merge(
for $file in $files

map:entry(local:getFilename($file), map{bpathb : $file, bkeynameb,
local:getKeyName($file))

)



At this point $imageMap is a map where each entrybs value is also a map.



I then iterate over the map to find images not referenced from any topics in
my doc set:



let $orphanMap := map:merge(
for $key in map:keys($imageMap)

order by $key

return

if (local:notReferenced($orphanMap($key)(bkeynameb)))

then $orphanMap($key)

else ()

)



I then report the items in $orphanMap.



When I ran the code as shown I was surprised to only get one item in the map
even though there are 199 orphaned images in my test set.



My bug of course is that I forgot that I have to construct a new map
entrybwhat the code above does is add the map that is the value of the entry
to the result map. These maps all have the same keys so of course the default
combine behavior results in a single entry in the result map. Doh!



I clearly had it in my head that b$orphanMap($key)b would return the
*entry* with that key, not the *value* of the entry with that key.



What threw me at first was that the map:merge() worked because it was being
given map entries to merge, just not the right ones. If the values in the
input map had been something else then the map:merge() would have failed and
Ibd have immediately realized my mistake.



The correct code is:

let $orphanMap := map:merge(
for $key in map:keys($imageMap)

order by $key

return

if (local:notReferenced($orphanMap($key)(bkeynameb)))

then map:entry($key, $orphanMap($key))

else ()

)



Ibm also wondering if therebs any general source of XQuery coding patterns
for working with maps in non-trivial ways? I havenbt run across one but I
havenbt looked too hard yet.



Cheers,



Eliot

--

Eliot Kimber

http://contrext.com

Current Thread