Search code examples
dictionaryfor-loopxquerymarklogic

For loop in dictionary in XQuery


I am trying to loop over items and output number of occurrences for each one, but it outputs the same dictionary a number of times equal to the number of items. Is there a way same as using parentheses to just make the loops for let statements. I know that XQuery is a functional language and that I can use group by but it is not supported in marklogic.

xquery version "1.0-ml";
let $dict := map:map()
for $i in ('book1','book2','book2')
let $n := (if (map:contains($dict, $i)) then (map:get($dict, $i)) else (0))
let $dict1 := map:put($dict, $i, 1 + $n)
return $dict

Output:

<map:map>
<map:entry key="book1">
<map:value xsi:type="xs:integer">1</map:value>
</map:entry>
<map:entry key="book2">
<map:value xsi:type="xs:integer">2</map:value>
</map:entry>
</map:map>
map as 
XML
<map:map>
<map:entry key="book1">
<map:value xsi:type="xs:integer">1</map:value>
</map:entry>
<map:entry key="book2">
<map:value xsi:type="xs:integer">2</map:value>
</map:entry>
</map:map>
map as 
XML
<map:map>
<map:entry key="book1">
<map:value xsi:type="xs:integer">1</map:value>
</map:entry>
<map:entry key="book2">
<map:value xsi:type="xs:integer">2</map:value>
</map:entry>
</map:map>

Solution

  • You can let a throwaway variable and then put the for loop FLWOR processing for that variable, then return the $dict map:

    xquery version "1.0-ml";
    let $dict := map:map()
    let $_ :=
      for $i in ('book1','book2','book2')
      let $n := if (map:contains($dict, $i)) then map:get($dict, $i) else 0
      return map:put($dict, $i, 1 + $n)
    return $dict
    

    Since map:put() returns an empty sequence, you could also return a sequence with the for-loop processing and then the $dict:

    xquery version "1.0-ml";
    let $dict := map:map()
    return (
      for $i in ('book1','book2','book2')
      let $n := if (map:contains($dict, $i)) then map:get($dict, $i) else 0
      return map:put($dict, $i, 1 + $n), 
      $dict
    )
    

    A more concise version of the same processing:

    xquery version "1.0-ml";
    let $dict := map:map()
    let $_ := ('book1','book2','book2') ! map:put($dict, ., 1 + head((map:get($dict, .), 0)))
    return $dict