Maps are immutable: map:put(), map:remove() and map:merge() return new maps; map:get() (or $m?key), map:contains(), map:keys(), map:size(), map:entry(), map:find() and map:for-each() read them. Keys compare like eq (1 and 1.0 are one key, "1" another). Build a lookup table once and probe it often:
xp 'map:merge(//book ! map:entry(string(@id), string(title)))?b3' \
'map:merge(//book ! map { string(published): string(@id) }, map { "duplicates": "combine" })' \
'map { 1: "x", 1.0: "y" }'Output
map:merge(//book ! map:entry(string(@id), string(title)))?b3 => "Salt and Saffron"
map:merge(//book ! map { string(published): string(@id) }, map { "duplicates": "combine" }) =>
map{"2024":("b1","b6"),"2025":("b2","b5"),"2026":"b4","2023":"b3"}
map { 1: "x", 1.0: "y" } => error XQDY0137The duplicates option combine keeps all values for a shared key, grouping books by year in one expression (use-first is the default, reject fails). Key order is undefined in 3.1 (2026 precedes 2023 here), so never rely on it; the 4.0 draft makes maps keep insertion order.
Map Functions
Maps are collections of key/value entries, written with curly braces (e.g. {"a":1,"b":2}); like arrays, they were introduced in XPath 3.1. The map: functions below are evaluated against:| let $m := {"a":1,"b":2,3:"c"} return … |
| map:get($m,3) | c |
| map:contains($m,"b") | true |
| map:keys($m) | a b 3 |
| map:size($m) | 3 |
| map:put($m,4,"d") | {"a": 1, "b": 2, "3": "c", "4": "d"} |
| map:remove($m,"a") | {"b": 2, "3": "c"} |
| map:merge((map:entry("a",100), map:entry("b",200))) | {"a": 100, "b": 200} |
| map:find([$m,$m],"a") | [1, 1] |
| map:for-each($m, function($k,$v){$k||$v}) | a1 b2 3c |
merged-01-01-ch03-map-functions.xpath.txt
let $m := {"a":1,"b":2,3:"c"}
return map:get($m,3)
Output
c