【问题标题】:Idiomatic way of checking if a key on a map has a value检查地图上的键是否具有值的惯用方法
【发布时间】:2012-08-01 23:30:57
【问题描述】:

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

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

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

【问题讨论】:

    标签: clojure


    【解决方案1】:

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

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

    或者你做了什么。

    【讨论】:

      【解决方案2】:

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

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

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

      【讨论】:

      • 好主意,我喜欢。顺便说一句,在代码中,您的最终调用似乎与您在第一行中定义的 seq-of-maps 不匹配。
      【解决方案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" 的地图

      【讨论】:

      • 考虑到这一点,这似乎并没有得到您需要的确切行为,因为它将返回一组唯一唯一地图的具体集合,而不是可能包含重复的流。
      【解决方案4】:

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

      【讨论】:

        【解决方案5】:
        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 所说,我认为惯用方式因个人意见而异。 我有时使用-> 宏来提取嵌套括号。

        【讨论】:

          【解决方案6】:

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

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

          【讨论】:

            【解决方案7】:

            只需将zero? 替换为(partial = "hi"),如下所示:

            => (filter (comp (partial = "hi") :bar) seq-of-maps)
            

            【讨论】:

              猜你喜欢
              • 2017-07-03
              • 1970-01-01
              • 2021-06-30
              • 2022-01-15
              • 1970-01-01
              • 2016-01-10
              • 2017-12-04
              • 2019-09-21
              相关资源
              最近更新 更多