如何在Clojure中过滤持久映射?
我有一个要筛选的持久映射。大概是这样的:如何在Clojure中过滤持久映射?,clojure,Clojure,我有一个要筛选的持久映射。大概是这样的: (filter #(-> % val (= 1)) {:a 1 :b 1 :c 2}) 上面显示为([:a1][:b1])(一个懒散的映射条目序列)。但是我想成为get{:a1:b1} 如何过滤贴图,使其保持为贴图,而不必从一系列贴图条目重新构建它 (into {} (filter #(-> % val (= 1)) {:a 1 :b 1 :c 2})) 当然,这确实可以从一系列映射条目重建映射,但是没有办法绕过它。如果要按值筛选条目,则
(filter #(-> % val (= 1)) {:a 1 :b 1 :c 2})
上面显示为([:a1][:b1])
(一个懒散的映射条目序列)。但是我想成为get{:a1:b1}
如何过滤贴图,使其保持为贴图,而不必从一系列贴图条目重新构建它
(into {} (filter #(-> % val (= 1)) {:a 1 :b 1 :c 2}))
当然,这确实可以从一系列映射条目重建映射,但是没有办法绕过它。如果要按值筛选条目,则必须逐个检查条目,以查看哪些值与谓词匹配,哪些不匹配
更新(见以下评论):
通过新引入的keep
函数,您可以看到该函数的源代码(如果您想备份,在Clojure 1.1中应该可以正常工作),如果您不使用nil
作为键,这似乎是一种很好的方法:
此外,如果确实看到与重建贴图相关的减速,则可以在重建步骤中使用瞬态贴图:
(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}
根据您对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ł的版本相比,我认为您不会从中获得太多好处。需要遍历所有条目,但可以利用Clojures持久映射:
(apply dissoc my-map (for [[k v] my-map :when (not= v 1)] k))
还有一个:
(let [m {:a 1 :b 2 :c 1}]
(select-keys m (for [[k v] m :when (= v 1)] k)))
基于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}}
下面是另一个使用
reducekv
(defn filter-kv [pred map]
(reduce-kv (fn [accumulator key value]
(if (pred key value)
(assoc accumulator key value)
accumulator)) {} map))
用法
理论上,你可以通过返回一个带有dissoc-ed键的映射(对应于不匹配的值),让它按值过滤,而无需重建。我希望有一种语言支持的方式来做这件事。好的,我明白你的意思。我会在一秒钟内增加两种方法,但请注意,你不太可能在绩效部门看到巨大的收益(除非你有一张非常大的地图,你只需要敲打一小部分钥匙)。嗯,事实上,与其说是“两种方法”,不如说是“一种方法做,一种方法不用担心重建”。并不是说你很可能需要担心。:-)转化为瞬态连接器!,所以我不确定循环重现是否有意义
(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"}}