23

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.

3
  • 1
    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. Commented Mar 26, 2013 at 14:37
  • 2
    I think it's time to accept one of these answers. My heart says dbyrne but my head says mtyaka :) Commented May 2, 2013 at 16:07
  • @joelittlejohn Hard to choose an answer because they're all right and useful and I think people are best served by reading them all! Commented May 6, 2013 at 13:30

4 Answers 4

40

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.
Sign up to request clarification or add additional context in comments.

Comments

31

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.

5 Comments

what will be the result if (:ground gridSettings) is nil?
The result of the whole expression will just be nil. There won't be an exception thrown.
(:ground nil) ;=> nil. Using -> unless you deliberately need something else also seems to be idiomatic Clojure.
But -> can be used with or to also nicely return a not-found value. (get-in grid-settings [:ground :variations] "not found!") vs. (-> grid-settings :ground :variations (or "not found!"))
You can also use -> when you need predicates that aren't keywords, i.e. when finding the next step on the navigation path requires calling a function.
14

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

Comments

3

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 

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.