4
votes

Given this map:

{:a nil 
   :b {:c nil 
       :d 2 
       :e {:f nil 
           :g 4}}}

I need a function to remove all nil values, so that the returned map is

{:b {:e {:g 4}
     :d 2}}

Or, when given:

{:a nil 
   :b {:c nil 
       :d nil 
       :e {:f nil 
           :g nil}}}

The result is:

nil

This question has an answer containing a function that supposedly works for nested maps, but that function fails when given a map that is nested more than one level deep.

3

3 Answers

8
votes

modification of answer from here https://stackoverflow.com/a/22186735/1393248


(defn remove-nils
  "remove pairs of key-value that has nil value from a (possibly nested) map. also transform map to nil if all of its value are nil" 
  [nm]
  (clojure.walk/postwalk 
   (fn [el]
     (if (map? el)
       (not-empty (into {} (remove (comp nil? second)) el))
       el))
   nm))
1
votes
(defn clean [m]
  (if (map? m)
    (let [clean-val (fn [[k v]]
                      (let [v' (clean v)]
                        (when-not (nil? v')
                          [k v'])))
          m' (->> (map clean-val m)
                  (remove nil?)
                  (into {}))]
      (when-not (empty? m') m'))
    m))
1
votes

Using specter you can do it like this:

(declarepath DEEP-MAP-VALS)
(providepath DEEP-MAP-VALS (if-path map? [(compact MAP-VALS) DEEP-MAP-VALS] STAY))

(setval [DEEP-MAP-VALS nil?] NONE  
{:a nil 
  :b {:c nil 
      :d 2 
      :e {:f nil 
      :g 4}}})

Please note it will return :com.rpl.specter.impl/NONE instead of nil if nothing is left.

This is partial reuse of this answer