如何在Clojure中过滤持久性地图?

时间:2010-05-02 15:08:51

标签: clojure

我有一个我要过滤的持久性地图。像这样:

(filter #(-> % val (= 1)) {:a 1 :b 1 :c 2})

以上是([:a 1] [:b 1])(一个懒惰的地图条目序列)。但是我希望得到{:a 1 :b 1}

如何过滤地图以使其保持地图而无需从一系列地图条目重建地图?

6 个答案:

答案 0 :(得分:47)

还有一个:

(let [m {:a 1 :b 2 :c 1}]
  (select-keys m (for [[k v] m :when (= v 1)] k)))

答案 1 :(得分:20)

(into {} (filter #(-> % val (= 1)) {:a 1 :b 1 :c 2}))

当然这个 从一系列地图条目重建地图,但没有办法绕过它。如果您要按值过滤条目,则必须逐个查看条目以查看哪些值与谓词匹配,哪些值不匹配。

更新(见下面的评论):

使用新引入的keep函数,您可以看到here的源代码(如果您想要向后移动,在Clojure 1.1中应该可以正常工作),这似乎是一个很好的方法如果您不使用nil作为密钥

(let [m {:a 1 :b 1 :c 2}]
  (apply dissoc m (keep #(-> % val (= 1) (if nil (key %))) m)))
; => {:a 1, :b 1}

此外,如果您确实看到与重建地图相关的减速,您可以在重建步骤中使用瞬态地图:

(persistent! (loop [m (transient {})
                    to-go (seq [[:a 1] [:b 2]])]
               (if to-go
                 (recur (apply assoc! m (first to-go))
                        (next to-go))
                 m)))
; => {:a 1, :b 2}

答案 2 :(得分:3)

根据您对MichałMarczyk的评论:

(defn filter* [f map]
  (reduce (fn [m [k v :as x]]
            (if-not (f x)
              (dissoc m k)
              m))
          map map))

user> (filter* #(-> % val (= 1)) {:a 1 :b 1 :c 2})
{:a 1, :b 1}

我不认为你会对Michał的版本获得太多收获。

答案 3 :(得分:3)

需要遍历所有条目,但可以利用Clojures持久映射:

(apply dissoc my-map (for [[k v] my-map :when (not= v 1)] k))

答案 4 :(得分:1)

我根据kotarak的版本尝试了自己的宏。它是我的第一个做有用的宏,所以请耐心等待,欢迎评论。

(defmacro filter-map [bindings pred m]
  `(select-keys ~m
    (for [~bindings ~m
      :when ~pred]
      ~(first bindings)
    )
  )
)

实施例

user=> (filter-map [key val] (even? (:attr val)) {:a {:attr 2} :b {:attr 3} :c {:attr 4}})
{:c {:attr 4}, :a {:attr 2}}

答案 5 :(得分:0)

这里还有一个使用reduce-kv

(defn filter-kv [pred map]
  (reduce-kv (fn [accumulator key value]
               (if (pred key value)
                 (assoc accumulator key value)
                 accumulator)) {} map))

用法

(filter-kv (fn [key _]
             (not (= key "a"))) {"a" {:some "a"}
                                 "b" {:some "b"}
                                 "c" {:some "c"}})

>> {"b" {:some "b"}
    "c" {:some "c"}}