Maps
Prerequisites: Lists and vectors
A map associates keys with values. It's the main way to represent information in Clojure — where other languages define a class, Clojure uses a map.
(def user {:name "Ada" :age 36 :city "London"})
(println (:name user))
(println (get user :age))
(println (user :city)) ; a map is a function of its keys
(println (get user :email "n/a"))
(println (count user))
(println (keys user))
(println (vals user))
(println (contains? user :age))
Ada
36
London
n/a
3
(:name :age :city)
(Ada 36 London)
true
Keywords are the usual keys, but any value can be a key:
{"id" 1, 42 "answer", [0 0] :origin}.
"Changing" a map
(def stock {:apples 3})
(println (assoc stock :pears 5))
(println (assoc stock :apples 10 :plums 2))
(println (dissoc {:a 1 :b 2} :a))
(println (update stock :apples inc))
(println (update stock :apples + 10))
(println (merge {:a 1 :b 2} {:b 20 :c 30}))
(println (select-keys {:a 1 :b 2 :c 3} [:a :c]))
(println stock)
{:apples 3, :pears 5}
{:apples 10, :plums 2}
{:b 2}
{:apples 4}
{:apples 13}
{:a 1, :b 20, :c 30}
{:a 1, :c 3}
{:apples 3}
assocadds or replaces keys;dissocremoves them.updateapplies a function to the current value —(update m :k inc).mergecombines maps; later values win.- As always,
stockitself never changed.
Looping over a map
(def prices {:coffee 3.5 :tea 2.8 :cake 4.2})
(doseq [[item price] prices]
(println (name item) "costs" price))
(println (into {} (map (fn [[k v]] [k (* v 2)]) prices)))
(println (update-vals prices #(* % 2)))
(println (reduce + (vals prices)))
coffee costs 3.5
tea costs 2.8
cake costs 4.2
{:coffee 7.0, :tea 5.6, :cake 8.4}
{:coffee 7.0, :tea 5.6, :cake 8.4}
10.5
A map is a sequence of [key value] entries. [item price] in
doseq pulls each entry apart (destructuring, covered fully in Level
2). update-vals (Clojure 1.11+) transforms every value.
Order
Small maps (up to 8 entries) happen to keep insertion order; larger ones
don't. Never rely on map order — use sorted-map when you need keys
sorted:
(println (sorted-map :c 3 :a 1 :b 2))
(println (into (sorted-map) {:zeta 1 :alpha 2}))
{:a 1, :b 2, :c 3}
{:alpha 2, :zeta 1}
Practice
Count how many times each word appears in "a b a c b a" and print
the result.
Show solution
(require '[clojure.string :as str])
(def words (str/split "a b a c b a" #" "))
(println (reduce (fn [counts w] (update counts w (fnil inc 0))) {} words))
(println (frequencies words))
{a 3, b 2, c 1}
{a 3, b 2, c 1}
(fnil inc 0) is inc, but treats a missing (nil) count as 0. The
built-in frequencies does the whole job.