【问题标题】:Remove key from map based on predicate clojure基于谓词clojure从地图中删除键
【发布时间】:2026-01-04 04:25:02
【问题描述】:

假设我有一个地图m,比如{"good1" 1, "bad1" 1, "good2" 2, "bad2" 2},我想根据地图键上的某些谓词删除条目,一种方法是:

(defn dissoc-by [f m] (->> m (filter (complement f)) (into {})))
(dissoc-by #(.contains (first %1) "bad") m)
 => {"good1" 1, "good2" 2}

在 clojure 中有更惯用的方法吗?

【问题讨论】:

    标签: clojure


    【解决方案1】:

    给定

    (def data {"good1" 1, "bad1" 1, "good2" 2, "bad2" 2})
    

    定义

    (defn remove-keys [pred m]
      (apply dissoc m (filter pred (keys m))))
    

    然后

    (remove-keys #(string/includes? % "bad") data)
    => {"good1" 1, "good2" 2}
    

    类似的函数在The Clojure Cookbook 中定义。

    【讨论】:

      【解决方案2】:

      这是一种非常正常的处理方式,其中一个更正是映射中的序列是一对[键值]而不仅仅是键的序列,因此您的过滤器函数需要显式使用键

      user> (defn dissoc-by [f m] (->> m (filter #(f (first %))) (into {})))
      #'user/dissoc-by
      user> (dissoc-by #(.contains % "good") m)
      {"good1" 1, "good2" 2}
      

      如果您想了解它和use a transducer,可以通过消除分配用于将数据从一个步骤移动到下一步的中间序列来提高此功能的效率。

      user> (defn dissoc-by [f m]
              (into {} (filter #(f (first %))) m))
      #'user/dissoc-by
      user> (dissoc-by #(.contains % "good") m)
      {"good1" 1, "good2" 2}
      

      into 接受一个可选的中间参数,一个转换器函数,它可以在数据被注入集合时过滤或转换数据。

      【讨论】:

      • 好收获!我也注意到了这一点,但决定也许更通用的解决方案是将键值对传递给谓词。我还意识到函数的命名约定意味着谓词应该删除,而不是保留元素。
      • 是的,问题也是如此。 [原文]
      • 选择匹配输入还是输出诶?
      • 这里使用into的问题是dissoc-by每次被调用都会构造一个全新的结构。即使您实际上不删除任何内容,也会复制整个地图结构,这是一种浪费。这与 Thumbnail 的回答相反,后者(dissocing 仅删除那些键)允许新地图与原始地图共享结构。根据您的应用程序,这可能会对内存使用产生巨大影响。
      • 如果我理解正确,键将是相同的对象,值将是相同的对象。这些是最有可能变大的部分。它自身的地图结构在大小上是有界的,因此它的大小是有限制的。由于此代码方法限制生成要删除的项目的中间列表将变得越来越大,因此通常很难说对于特殊情况什么更好。请记住,clojure 映射中可能存在的内部结构数量有限。
      【解决方案3】:

      这是完全符合您需要的代码:

      (def data {"good1" 1, "bad1" 1, "good2" 2, "bad2" 2})
      
      (into (empty data) 
            (filter (fn [[k v]] (clojure.string/includes? k "good")) 
                    data))
      
      {"good1" 1, "good2" 2}
      

      【讨论】: