用于检查地图上的某个键是否具有值的惯用方法

时间:2012-08-01 23:30:57

标签: clojure

检查地图上的某个键是否有值的惯用方法是什么?例如,如果我们有:

=> (def seq-of-maps [{:foo 1 :bar "hi"} {:foo 0 :bar "baz"}])

要查找所有地图:foo == 0,我喜欢:

=> (filter (comp zero? :foo) seq-of-maps)
({:foo 0, :bar "baz"})

但如果我想找到所有地图:bar ==“hi”,我能想到的最好的是:

=> (filter #(= (:bar %) "hi") seq-of-maps)
({:foo 1, :bar "hi"})

我觉得它不太可读。是否有更好/更惯用的方式呢?

6 个答案:

答案 0 :(得分:10)

惯用语是主观的,但我会做

=> (filter (comp #{"hi"} :bar) seq-of-maps)

或你做了什么。

答案 1 :(得分:5)

我个人喜欢重构这种事情以使用明确命名的高阶函数:

(def seq-of-maps [{:foo 1 :bar "hi"} {:foo 0 :bar "baz"}])

(defn has-value [key value]
  "Returns a predicate that tests whether a map contains a specific value"
  (fn [m]
    (= value (m key))))

(filter (has-value :bar "hi") seq-of-maps)
=> ({:foo 1, :bar "hi"})

缺点是它为您提供了额外的功能定义来管理和维护,但我认为优雅/代码可读性是值得的。如果您多次重复使用谓词,从性能角度来看,这种方法也非常有效。

答案 2 :(得分:3)

clojure.set / index也可以在这里使用

((index seq-of-maps [:foo]) {:foo 0})
((index seq-of-maps [:bar]) {:bar "hi"})

如果您愿意,可以将其包装在函数中

(defn select-maps [xrel m]
   ((index xrel (keys m)) m))

然后

(select-maps seq-of-maps {:foo 0})
(select-maps seq-of-maps {:bar "hi"})

两者都有效 - 您还可以使用索引来请求具有多个键/值的地图,即:

(select-maps seq-of-maps {:foo 0 :bar "baz"}) 

选择包含foo 0和bar“baz”

的所有地图

答案 3 :(得分:2)

将匿名函数传递给过滤器的第三个示例似乎是一种用于查找具有给定值的地图的更自觉的方法。我发现它很容易阅读。

答案 4 :(得分:2)

user> (def seq-of-maps [{:foo 1 :bar "hi"} {:foo 0 :bar "baz"}])
#'user/seq-of-maps
user> (filter #(-> % :bar (= "hi")) seq-of-maps)
({:foo 1, :bar "hi"})

正如Pepijn所说,我认为惯用的方式根据个人意见而有所不同。 我有时会使用->宏来提取嵌套的括号。

答案 5 :(得分:0)

您的代码对我来说很好。其他可能的解决方案是使用for宏,如下所示

(for [m seq-of-maps 
      :let [v (:bar m)] 
      :when (= v "hi")] 
   m)