Joakim Tall
Joakim Tall

Reputation: 426

Clojure get nested map value

So I'm used to having a nested array or map of settings in my applications. I tried setting one up in Clojure like this:

(def gridSettings
  {:width 50
   :height 50
   :ground {:variations 25}
   :water {:variations 25}
   })

And I wondered if you know of a good way of retrieving a nested value? I tried writing

(:variations (:ground gridSettings))

Which works, but it's backwords and rather cumbersome, especially if I add a few levels.

Upvotes: 23

Views: 14012

Answers (4)

01es
01es

Reputation: 5410

Maps are partial functions (as in not total). Thus, one can simply apply them as functions. Based on the map from the question:

(gridSettings :ground)
;=> {:variations 25}

The result is a map. So, it can be applied again, which results in a very similar (but not backwards) "syntax" as proposed in the question:

((gridSettings :ground) :variations)
;=>25 

Upvotes: 3

Ankur
Ankur

Reputation: 33637

Apart from what other answers has mentioned (get-in and -> macro), sometimes you want to fetch multiple values from a map (nested or not), in those cases de-structuring can be really helpful

(let [{{gv :variations} :ground
       {wv :variations} :water} gridSettings]
  [gv wv]) 

Upvotes: 14

dbyrne
dbyrne

Reputation: 61011

You can use the thread-first macro:

(-> gridSettings :ground :variations)

I prefer -> over get-in except for two special cases:

  • When the keys are an arbitrary sequence determined at runtime.
  • When supplying a not-found value is useful.

Upvotes: 31

mtyaka
mtyaka

Reputation: 8850

That's what get-in does:

(get-in gridSettings [:ground :variations])

From the docstring:

clojure.core/get-in
([m ks] [m ks not-found])
  Returns the value in a nested associative structure,
  where ks is a sequence of keys. Returns nil if the key
  is not present, or the not-found value if supplied.

Upvotes: 40

Related Questions