Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to write a dissoc-in command for clojure?

Tags:

clojure

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

like image 935
zcaudate Avatar asked Jan 23 '13 19:01

zcaudate


4 Answers

I write this using update-in:

(update-in {:a {:b 0 :c 1}} [:a] dissoc :b) 

=>

{:a {:c 1}} 
like image 101
Norman Richards Avatar answered Sep 23 '22 07:09

Norman Richards


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 {}} 
like image 33
sloth Avatar answered Sep 24 '22 07:09

sloth


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))
like image 35
Symfrog Avatar answered Sep 23 '22 07:09

Symfrog


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]) 
;;=> {}    
like image 20
zcaudate Avatar answered Sep 22 '22 07:09

zcaudate