从地图中删除零值?

edo*_*lin 28 clojure

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

例如:

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

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

({:a 1} {:b 2})
Run Code Online (Sandbox Code Playgroud)

我想拥有:

{:a 1, :b 2}
Run Code Online (Sandbox Code Playgroud)

Jür*_*zel 58

your for list comprehension返回一个LIST of maps,因此你需要将这个列表作为可选参数应用于merge函数:

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

通过将地图过滤为序列并连接到地图中,可以获得更简洁的解决方案:

user> (into {} (filter second record))
{:a 1, :b 2}  
Run Code Online (Sandbox Code Playgroud)

不要删除错误值:

user> (into {} (remove (comp nil? second) record))
{:a 1, :b false}  
Run Code Online (Sandbox Code Playgroud)

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

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

  • +1(过滤器秒......)方法.非常聪明. (7认同)
  • 不幸的是`(过滤秒......)方法不起作用.`(into {}(filter second {:a true:b false}))`给`{:a true}` - (7认同)
  • 这会更惯用一点:“(into {} (remove (comp nil? val) record))” (4认同)
  • 使用“val”而不是“second”会更好。`val` 让人一眼就能看出 record 是一系列映射条目,即它是一个映射。 (2认同)

Eel*_*lco 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)))
Run Code Online (Sandbox Code Playgroud)


thn*_*tos 5

你可以把它压成地图:

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


Xav*_*avi 5

Jürgen Hötzel 解决方案改进以修复 nil/false 问题

(into {} (filter #(not (nil? (val %))) {:a true :b false :c nil}))
Run Code Online (Sandbox Code Playgroud)

@thnetos 解决方案的较短版本

(into {} (remove #(nil? (val %)) {:a true :b false :c nil}))
Run Code Online (Sandbox Code Playgroud)


Mar*_*lar 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)))
Run Code Online (Sandbox Code Playgroud)

至于@ 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
Run Code Online (Sandbox Code Playgroud)