Skip to content
AgileLogics
5 min read

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-in reads a value by path (vector indexes work too).
  • assoc-in sets a value by path, creating maps along the way if needed.
  • update-in applies a function at a path.
  • The original company is 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