Nested Data
Prerequisites: Maps, Lists and vectors
Real data is nested: a map of vectors of maps. Clojure has functions that reach into nested structures by a path of keys.
(def company
{:name "Acme"
:address {:city "Berlin" :zip "10115"}
:employees [{:name "Ada" :skills #{:clojure}}
{:name "Linus" :skills #{:c}}]})
(println (get-in company [:address :city]))
(println (get-in company [:employees 1 :name]))
(println (get-in company [:address :country] "unknown"))
(def moved (assoc-in company [:address :city] "Munich"))
(println (:address moved))
(def trained (update-in company [:employees 0 :skills] conj :java))
(println (sort (get-in trained [:employees 0 :skills])))
(println (get-in company [:address :city]))
Berlin
Linus
unknown
{:city Munich, :zip 10115}
(:clojure :java)
Berlin
get-inreads a value by path (vector indexes work too).assoc-insets a value by path, creating maps along the way if needed.update-inapplies a function at a path.- The original
companyis unchanged — only the parts along the path were copied; everything else is shared.
Working with a list of records
(def people
[{:name "Ada" :age 36 :team :core}
{:name "Linus" :age 28 :team :kernel}
{:name "Grace" :age 45 :team :core}])
(println (map :name people))
(println (filter #(= :core (:team %)) people))
(println (map :name (sort-by :age people)))
(println (reduce + (map :age people)))
(println (group-by :team people))
(println (update-vals (group-by :team people) count))
(Ada Linus Grace)
({:name Ada, :age 36, :team :core} {:name Grace, :age 45, :team :core})
(Linus Ada Grace)
109
{:core [{:name Ada, :age 36, :team :core} {:name Grace, :age 45, :team :core}], :kernel [{:name Linus, :age 28, :team :kernel}]}
{:core 2, :kernel 1}
Keywords as functions shine here: (map :name people) extracts a
field, (sort-by :age people) sorts by one. This style — plain data
plus generic functions — is the heart of Clojure.
Practice
Give everyone in people on the :core team a raise by adding
:salary 100 to their map, and print the names and salaries.
Show solution
(def people
[{:name "Ada" :team :core}
{:name "Linus" :team :kernel}
{:name "Grace" :team :core}])
(def updated
(mapv #(if (= :core (:team %)) (assoc % :salary 100) %) people))
(doseq [p updated]
(println (:name p) (:salary p "no change")))
Ada 100
Linus no change
Grace 100