查找地图是否包含多个键

10

我是Clojure的新手,想知道是否有一种方法来测试一个Map是否具有多个键。我注意到contains?仅检查一个键。

我的目标是:

(def mario 
    {:position {:x 1 :y 2}
     :velocity {:x 2 :y 0}
     :mass 20})

;;Test if mario has a position and a velocity
(contains-many? mario :position :velocity) ;;true

;;Test if mario has a mass and a jump-height
(contains-many? mario :mass :jump-height) ;;false

基本上,在Clojure库中是否有像contains-many?这样的函数,如果没有,您将如何实现contains-many?函数?

4个回答

23

虽然正确答案已经显示,但我想指出另一种优雅的解决方案,它假设您了解地图值。当您确定它们是真实的(也就是说不是 nil 和不是 false )时:

<code><code>(every? m ks)
</code></code>

这是因为地图是(一元)函数,它会返回相应参数的值。但请注意({:x nil} :x) => nil


1
地图是函数?哇!好吧,我确定我的值不是nil,但它们可能是false。所以,我不认为这次我能用这个技巧,但我肯定会记住它,谢谢! - Hassan Hayat

12

我不知道有任何函数可以做到这一点。还需要定义是否希望地图包含每个键或只是一些键。我选择了每个情况,如果您想要一些版本,请将every?替换为some?

我的直接、未优化的版本是:

(defn contains-many? [m & ks]
  (every? #(contains? m %) ks))

已经测试过的内容:

(deftest a-test
  (testing "Basic test cases"
    (let [m {:a 1 :b 1 :c 2}]
      (is (contains-many? m :a))
      (is (contains-many? m :a :b))
      (is (contains-many? m :a :b :c))
      (is (not (contains-many? m :a :d)))
      (is (not (contains-many? m :a :b :d))))))

编辑:使用noisesmith的建议进行了简化


太棒了!谢谢。是的,我想要一个函数来测试地图是否拥有每个键。谢谢。不过,我有一个问题:这个语法是什么意思:“#(contains?m%)”?这像是柯里化或模式匹配之类的东西吗? - Hassan Hayat
1
这是一种语法糖,用于创建一个带有一个参数的匿名函数(该参数由%表示)。它等同于(fn [k] (contains? m k))。https://clojuredocs.org/clojure.core/fn - sbensu
很酷!谢谢。我明白了。语法糖确实使它更加简洁易读。 - Hassan Hayat
6
检查ks中的每个元素是否都包含在m中:(every? #(contains? m %) ks) - noisesmith

3
您可以利用 clojure.set/subset? 进行优化。
(clojure.set/subset?
  #{:position :velocity}
  (set (keys mario)))

2

这里有另一种使用 clojure.set 的解决方案,可以处理非真值:

(require '[clojure.set :as set])

(defn contains-many? [m & ks]
  (empty? (set/difference (set ks) (set (keys m)))))

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接