Recursively mutating values in a map of maps with Specter

Viewed 264

complete Clojure newbie here. This is a simple question, but I cant seem to get it:

Given that I have a nested hasmap of unknown depth, how do I use Specter's transform() to mutate the values of the data structure? I imagine a recursive-path is required here, but I cant get it to work. a working example is what I'm after and unfortunately, there isn't one in the docs.

(there is an example for set-val on a recursive map, but I don't know how to transform that into a transform use-case)

EDIT: More details were requested, so here they are: I'm interested in a transform form that can mutate all the values of a nested maps - of any depth). for example, this transform would be able to increment all the values in the following maps (and any other nested map):

{:a 1 :b {:c 2 :d {:e 3}}} 
AND
{:a 1 :b {:c 2}}
AND
{:a 1}

the line of code I'm interested in might look sth like this:

(transform <missing selector here> inc data)
2 Answers

Using the linked example:

(def MAP-NODES
   (recursive-path [] p
     (if-path map?
       (continue-then-stay MAP-VALS p))))
(transform [MAP-NODES MAP-VALS number?] inc data)

Be sure to always study the Clojure CheatSheet. For something this simple, it might be easiest to just use clojure.walk/postwalk

(ns tst.demo.core
  (:use tupelo.core tupelo.test)
  (:require [clojure.walk :as walk]))

(dotest
  (let [data   {:a 1 :b {:c 2 :d {:e 3}}}
        result (walk/postwalk (fn [item]
                                (if (int? item)
                                  (inc item)
                                  item))
                 data)]
    (is= result {:a 2, :b {:c 3, :d {:e 4}}})))

For more advanced problems, you may be interested in the function walk-with-parents, which allows you to inspect all items on the path from the root node through to the current node.

Related