从地图中删除零值?

时间:2010-10-14 21:15:59

标签: clojure

我有一个Clojure地图,可能包含nil的值,我正在尝试编写一个函数来删除它们,但没有太大的成功(我是新手)。

E.g:

(def record {:a 1 :b 2 :c nil})
(merge (for [[k v] record :when (not (nil? v))] {k v}))

这导致了一系列地图,这不是我对合并的期望:

({:a 1} {:b 2})

我想:

{:a 1, :b 2}

9 个答案:

答案 0 :(得分:54)

你的 for list comprehension返回一个LIST的map,所以你需要将这个列表作为可选参数应用于merge函数:

user> (apply merge (for [[k v] record :when (not (nil? v))] {k v}))
{:b 2, :a 1}      

通过将地图过滤为序列并连接到地图中来提供更简洁的解决方案:

user> (into {} (filter second record))
{:a 1, :b 2}  

不要删除 false 值:

user> (into {} (remove (comp nil? second) record))
{:a 1, :b false}  

使用 dissoc 允许持久数据共享,而不是创建一个全新的地图:

user> (apply dissoc                                                                                            
       record                                                                                                  
       (for [[k v] record :when (nil? v)] k))
{:a 1, :b 2}  

答案 1 :(得分:7)

这是一个适用于嵌套地图的工具:

(defn remove-nils
  [m]
  (let [f (fn [[k v]] (when v [k v]))]
    (postwalk (fn [x] (if (map? x) (into {} (map f x)) x)) m)))

答案 2 :(得分:5)

你可以把它压成地图:

(into {} (remove (fn [[k v]] (nil? v)) {:a 1 :b 2 :c nil}))
=> {:a 1 :b 2}

答案 3 :(得分:5)

@ Eelco回答的一个变种:

(defn remove-nils [m]
  (let [f (fn [x]
            (if (map? x)
              (let [kvs (filter (comp not nil? second) x)]
                (if (empty? kvs) nil (into {} kvs)))
              x))]
    (clojure.walk/postwalk f m)))

至于@ broma0的观点,它忽略了任何空地图。

user> (def m {:a nil, :b 1, :c {:z 4, :y 5, :x nil}, :d {:w nil, :v nil}})
user> (remove-nils m)
{:b 1, :c {:z 4, :y 5}}
user> (remove-nils {})
nil

答案 4 :(得分:4)

JürgenHötzel解决方案经过精心修复以解决错误/错误问题

(into {} (filter #(not (nil? (val %))) {:a true :b false :c nil}))

@thnetos解决方案的缩短版本

(into {} (remove #(nil? (val %)) {:a true :b false :c nil}))

答案 5 :(得分:2)

虽然Jürgen的(过滤第二记录)方法得到了我对Niftiest Clojure Trick的投票,但我认为我还会采用另一种方式,这次使用select-keys

user> (select-keys record (for [[k v] record :when (not (nil? v))] k))
{:b 2, :a 1}

答案 6 :(得分:2)

reduce-kv也可以用来删除键

(reduce-kv (fn [m key value]
                (if (nil? value)
                  (dissoc m key)
                  m))
            {:test nil, :test1 "hello"}
            {:test nil, :test1 "hello"})

答案 7 :(得分:1)

您可以使用reduce。

user> (reduce (fn [m [k v]] (if (nil? v) m (assoc m k v))) {} record)
{:b 2, :a 1}

如果由于某种原因你想保留顺序(通常在地图中不重要),你可以使用dissoc

user> (reduce (fn [m [k v]] (if (nil? v) (dissoc m k) m)) record record)
{:a 1, :b 2}

答案 8 :(得分:1)

这是一个适用于地图和矢量的人:

(defn compact
  [coll]
  (cond
    (vector? coll) (into [] (filter (complement nil?) coll))
    (map? coll) (into {} (filter (comp not nil? second) coll))))