Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Clojure: How to apply a function to every value in a nested map and update?

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.

like image 943
gini09 Avatar asked Oct 18 '25 22:10

gini09


1 Answers

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.
like image 90
Alan Thompson Avatar answered Oct 20 '25 19:10

Alan Thompson



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!