【问题标题】:Remove nil values from a map?从地图中删除零值?
【发布时间】:2010-10-14 21:15:58
【问题描述】:

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

例如:

(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}

【问题讨论】:

    标签: clojure


    【解决方案1】:

    您的 for 列表推导返回一个地图列表,因此您需要将此列表作为可选参数应用到合并函数:

    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}  
    

    【讨论】:

    • 不幸的是,(filter second ...) 方法不起作用。 (into {} (filter second {:a true :b false})){:a true}-
    • into 使用瞬变,因此尽管不是同一张地图,但它很节省空间
    • 最好使用val 而不是secondval 让人一眼就能看出记录是一系列映射条目,即它是一个映射。
    • 这会更惯用:(into {} (remove (comp nil? val) record))
    • 不仅如此:使用val 代替second 还消除了将每个MapEntry 隐式转换为seq 的开销。虽然更好的语义匹配通常是val 的更强论据。
    【解决方案2】:

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

    (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)))
    

    【讨论】:

    • 我的意思是,它适用于常规地图,但也适用于例如{:foo {:bar nil :baz "ba"}}
    • 使用包含所有 nil 的地图,这应该真正返回 nil。例如,(remove-nils {:foo {:bar nil :baz nil}}) 应该返回 nil,而是返回 {:foo {}}。我们如何修改此答案以解决具有 all nil 值的地图问题?
    【解决方案3】:

    @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】:

      你可以把它压缩成地图:

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

      【讨论】:

        【解决方案5】:

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

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

        @thnetos 解决方案的更短版本

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

        【讨论】:

          【解决方案6】:

          虽然 Jürgen 的(过滤第二条记录)方法让我投票支持 Niftiest Clojure Trick,但我想我会另辟蹊径,这次使用 select-keys

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

          【讨论】:

            【解决方案7】:

            reduce-kv 也可以用来移除键

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

            【讨论】:

              【解决方案8】:

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

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

              【讨论】:

                【解决方案9】:

                你可以使用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}
                

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 2017-06-13
                  • 1970-01-01
                  • 1970-01-01
                  • 2020-05-04
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2020-01-20
                  相关资源
                  最近更新 更多