HashMap
Calcit HashMap is a persistent, immutable hash map. In Rust it uses rpds::HashTrieMap. In JavaScript it is built on ternary-tree.
All map operations return new maps — the original is never mutated.
Quick Recipes
- Create:
{} (:a 1) (:b 2) - Access:
get m :a,contains? m :a - Modify:
assoc m :c 3,dissoc m :a,update m :a inc - Transform:
map-kv m f,merge m1 m2 - Keys/Values:
keys m,vals m,to-pairs m
Creating Maps
{} is a macro that takes key-value pairs:
let
m $ {}
:a 1
:b 2
:c 3
println m
; => ({} (:a 1) (:b 2) (:c 3))
Inline form:
let
m $ {} (:x 10) (:y 20)
println m
The low-level primitive &{} takes flat key-value pairs:
&{} :a 1 :b 2
Reading Values
let
m $ {} (:a 1) (:b 2) (:c 3)
println $ get m :a
; => 1
println $ get m :missing
; => nil
println $ contains? m :b
; => true
println $ count m
; => 3
println $ empty? m
; => false
Nested access with get-in
let
nested $ {} (:user $ {} (:name |Alice) (:age 30))
println $ get-in nested $ [] :user :name
; => Alice
Modifying Maps
All operations return a new map:
let
m $ {} (:a 1) (:b 2)
m2 $ assoc m :c 3
m3 $ dissoc m2 :b
m4 $ merge m $ {} (:d 4) (:e 5)
println m2
; => ({} (:a 1) (:b 2) (:c 3))
println m3
; => ({} (:a 1) (:c 3))
println m4
; => ({} (:a 1) (:b 2) (:d 4) (:e 5))
Nested update with assoc-in
; update a deeply nested value
assoc-in config $ [] :server :port $ 8080
Iterating & Transforming
map-kv — transform entries
Returns a new map. If the callback returns nil, the entry is dropped (used as filter):
let
m $ {} (:a 1) (:b 2) (:c 13)
doubled $ map-kv m $ fn (k v) ([] k (* v 2))
filtered $ map-kv m $ fn (k v)
if (> v 10) nil
[] k v
println doubled
; => ({} (:a 2) (:b 4) (:c 26))
println filtered
; => ({} (:a 1) (:b 2))
to-pairs — convert to set of pairs
let
m $ {} (:a 1) (:b 2)
println $ to-pairs m
; => (#{} ([] :a 1) ([] :b 2))
keys and vals
let
m $ {} (:x 10) (:y 20)
println $ keys m
; => (#{} :x :y)
println $ vals m
; => (#{} 10 20)
each-kv — side-effect iteration
each-kv config $ fn (k v)
println $ str k |: v
Querying
let
m $ {} (:a 1) (:b 2) (:c 3)
println $ includes? m 2
; => true (checks values)
println $ contains? m :a
; => true (checks keys)
Building from Other Structures
; from a list of pairs
; each pair is [key value]
foldl my-pairs ({}) $ fn (acc pair)
assoc acc (nth pair 0) (nth pair 1)
Using thread macro to build up a map (inserting as first arg to each step):
let
base $ {} (:a 1) (:b 2)
result $ merge base $ {} (:c 3) (:d 4)
println result
Common Patterns
Default value on missing key
let
m $ {} (:a 1) (:b 2)
val $ get m :missing
if (nil? val) :default val
; => :default
Counting occurrences
let
words $ [] :a :b :a :c :a :b
init $ {}
freq $ foldl words init $ fn (acc w)
let
cur $ get acc w
n $ if (nil? cur) 0 cur
assoc acc w (inc n)
println freq
; ({} (:a 3) (:b 2) (:c 1))
Merging with override
let
defaults $ {} (:host |localhost) (:port 3000) (:debug false)
overrides $ {} (:port 8080) (:debug true)
merge defaults overrides
; => ({} (:host |localhost) (:port 8080) (:debug true))
Implementation Notes
HashMap key iteration order is not guaranteed. Use to-pairs + sort if you need stable order. Tags (:kw) are the most common key type; string keys also work but tags are faster for equality checks.