Let's say there is a nested map like below: (partially nested only)
(def mymap {:a 10
:b {:ba 21, :bb 22 :bc 23}
:c 30
:d {:da 41, :db 42}})
How can I apply a function, say #(* % 2)
, and update every value in this map? That is without specifying any key. The result will look like this:
{:a 20,
:b {:ba 42, :bb 44, :bc 46},
:c 60,
:d {:da 82, :db 84}}
So far, I came up with this own function:
(defn map-kv [f coll] (reduce-kv (fn [m k v] (assoc m k (f v))) (empty coll) coll))
But I still need to specify a first-level key and can't apply to all first-level and second-level keys values.
In addition to postwalk, as Alan mentioned, it is trivial to recursively explore the map and update every key. Clojure provides a function called fmap
that simply applies a function to every value in a map. To use:
In project.clj, declare this dependency:
[org.clojure/algo.generic "0.1.2"]
And in your code, then require:
(require '[clojure.algo.generic.functor :as f :only [fmap]])
Then define a function that will walk your map recursively:
(defn fmap*
[f m]
(f/fmap #(if (map? %)
(fmap* f %)
(f %))
m))
(fmap*
(partial * 2) ;; double every number
{:a 21 :b {:x 11 :y 22 :z {:p 100 :q 200}}})
=> {:a 42, :b {:x 22, :y 44, :z {:p 200, :q 400}}}
In case you don't want to have to include a non-core function, here's the code for fmap used on a map, from the clojure source (adapted for a defn):
(defn fmap [f m]
(into (empty m) (for [[k v] m] [k (f v)])))