18
votes

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.

4
Both mtaka's and dbyrne's answers are common ways to solve this issue. Use whichever strikes your fancy! I would note that get-in makes your intent clearer. - WolfeFan
Wow all good answers:) Thanks - Joakim Tall
I think it's time to accept one of these answers. My heart says dbyrne but my head says mtyaka :) - joelittlejohn
@joelittlejohn Hard to choose an answer because they're all right and useful and I think people are best served by reading them all! - Joakim Tall

4 Answers

36
votes

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.
29
votes

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.
12
votes

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]) 
3
votes

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