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.
You may wish to review the postwalk
function: https://clojuredocs.org/clojure.walk/postwalk
(def data
{:a 10
:b {:ba 21, :bb 22 :bc 23}
:c 30
:d {:da 41, :db 42}} )
(defn tx-nums [x]
(if (number? x)
(* 2 x)
x))
(postwalk tx-nums data) =>
{:a 20,
:b {:ba 42, :bb 44, :bc 46},
:c 60,
:d {:da 82, :db 84}}
Porthos3 makes a good point. The above will transform map keys as well as map values. If you want only values to change, you could use the map-vals
function from the Tupelo Clojure library (the Medley lib has a similar function).
(ns tst.demo.core
(:use demo.core tupelo.core tupelo.test)
(:require
[tupelo.core :as t]
[clojure.walk :as walk]))
(dotest
(let [data-2 {1 2
3 4}
tx-vals-fn (fn [item]
(if (map? item)
(t/map-vals item #(* 2 %))
item))
result (walk/postwalk tx-vals-fn data-2)]
(is= (spyx result) {1 4, 3 8})))
with result:
-------------------------------
Clojure 1.10.1 Java 13
-------------------------------
Testing tst.demo.core
result => {1 4, 3 8}
Ran 2 tests containing 1 assertions.
0 failures, 0 errors.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With