I'm looking to write a function that is similar to assoc-in but removes keys instead of adding it:
(dissoc-in {:a {:b 0}} [:a :b])
;;=> {:a {}}
I got up to here:
(def m {:a {:b {:c 1}}})
(assoc m :a (assoc (:a m) :b (dissoc (:b (:a m)) :c)))
;;=> {:a {:b {}}}
but the whole nested thing is messing with my head
I write this using update-in:
(update-in {:a {:b 0 :c 1}} [:a] dissoc :b)
=>
{:a {:c 1}}
How about:
(defn dissoc-in "Dissociates an entry from a nested associative structure returning a new nested structure. keys is a sequence of keys. Any empty maps that result will not be present in the new structure." [m [k & ks :as keys]] (if ks (if-let [nextmap (get m k)] (let [newmap (dissoc-in nextmap ks)] (if (seq newmap) (assoc m k newmap) (dissoc m k))) m) (dissoc m k)))
Example:
(dissoc-in {:a {:b 0 :c 1}} [:a :b])
Result:
{:a {:c 1}}
dissoc-in
was once part of clojure.contrib.core
, and is now part of core.incubator
.
If you want to keep empty maps, you can alter the code slightly:
(defn dissoc-in [m [k & ks :as keys]] (if ks (if-let [nextmap (get m k)] (let [newmap (dissoc-in nextmap ks)] (assoc m k newmap)) m) (dissoc m k)))
Example:
(dissoc-in {:a {:b {:c 0}}} [:a :b])
Result:
{:a {}}
Here is a general solution based on update-in:
(defn dissoc-in [m p]
(if (get-in m p)
(update-in m
(take (dec (count p)) p)
dissoc (last p))
m))
Being inspired by Dominic's code. I wrote a more succinct version
(defn dissoc-in
[m [k & ks]]
(if-not ks
(dissoc m k)
(assoc m k (dissoc-in (m k) ks))))
(dissoc-in {:a {:b {:c 1}}} [:a :b :c]) ;; => {:a {:b {}}}
Another version dissoc-in2 recursively removes empty maps
(defn dissoc-in2
[m [k & ks]]
(if-not ks
(dissoc m k)
(let [nm (dissoc-in2 (m k) ks)]
(cond (empty? nm) (dissoc m k)
:else (assoc m k nm)))))
(ut/dissoc-in {:a {:b {:c 3}}} [:a :b :c])
;;; => {:a {:b {}}}
(ut/dissoc-in2 {:a {:b {:c 3}}} [:a :b :c])
;;=> {}
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