Fork me on GitHub
#specter
<
2020-10-07
>
Petrus Theron09:10:05

How can I transform a map based on the key? E.g.

(let [key->xf {:a inc, :b :dec}]
  (S/transform [?] key->xf {:a 10, :b 20, :c 30}))
=> {:a 11, :b 19, :c 30}
I suspect I can use view or transformed for this, but not sure how. I found a FIND-KEYS navigator on SO that navigates to the values of matching keys, but it isn’t quite what I’m looking for:
(def FIND-KEYS
  "Like clojure.walk/postwalk but for Specter.
  Use as [FIND-KEYS (S/must :key)]."
  (S/recursive-path [] p
    (S/cond-path map?
      (S/continue-then-stay [S/MAP-VALS p])
      vector? [S/ALL p]
      S/STAY)))
E.g.
(S/select [FIND-KEYS (S/must :c)] {:a 123 :b {:c 456}})
=> 456
I guess I can do it with a plain-old reduce-kv, but hoping I could do it with Specter:
(defn transform-map
  [mappings m]
  (reduce-kv
    (fn [acc k vf]
      (if (contains? acc k)
        (update acc k vf)
        acc)) m mappings))

(transform-map {:a inc, :b dec} {:a 10, :b 20})
=> {:a 11, :b 19}

Jeff Evans14:10:40

(def key->xf {:a inc :b dec :c identity})
(s/transform [s/ALL] (fn [entry] (let [[k v] entry] [k ((k key->xf) v)])) {:a 10, :b 20, :c 30})
=> {:a 11, :b 19, :c 30}

Jeff Evans14:10:17

(`ALL` gives you key/value pairs in a vec)