14

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

=> (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"})

我觉得不太可读。有更好/更惯用的方法吗?

4

7 回答 7

15

惯用语是主观的,但我愿意

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

或者你做了什么。

于 2012-08-01T23:51:04.087 回答
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"})

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

于 2012-08-02T01:58:07.977 回答
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" 的地图

于 2012-08-02T00:48:41.807 回答
2

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

于 2012-08-02T00:07:57.847 回答
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 所说,我认为惯用方式因个人意见而异。我有时使用->宏来提取嵌套括号。

于 2012-08-02T00:49:42.307 回答
0

你的代码对我来说看起来不错。其他可能的解决方案是使用for宏,如下所示

(for [m seq-of-maps 
      :let [v (:bar m)] 
      :when (= v "hi")] 
   m)
于 2012-08-02T04:07:26.063 回答
0

只需像这样zero?替换(partial = "hi")

=> (filter (comp (partial = "hi") :bar) seq-of-maps)
于 2012-08-02T04:08:37.823 回答