clojure

How to select keys in nested maps in Clojure?


Let's say I have a map (m) like this:

(def m {:a 1 :b 2 :c {:d 3 :e 4} :e { ... } ....})

I'd like to create a new map only containing :a, :b and :d from m, i.e. the result should be:

{:a 1 :b 2 :d 3}

I know that I can use select-keys to easily get :a and :b:

(select-keys m [:a :b])

But what's a good way to also get :d? I'm looking for something like this:

(select-keys* m [:a :b [:c :d]])

Does such a function exists in Clojure or what's the recommended approach?


Solution

  • In pure Clojure I would do it like this:

    (defn select-keys* [m paths]
      (into {} (map (fn [p]
                      [(last p) (get-in m p)]))
            paths))
    
    (select-keys* m [[:a] [:b] [:c :d]]) ;;=> {:a 1, :b 2, :d 3}
    

    I prefer keeping the type of a path regular, so a sequence of keys for all paths. In clojure.spec this would read as

    (s/def ::nested-map (s/map-of keyword? 
                                  (s/or :num number? :map ::nested-map)))
    (s/def ::path (s/coll-of keyword?))
    (s/fdef select-keys*
            :args (s/cat :m ::nested-map 
                         :paths (s/coll-of ::path)))