如何在Clojure中的嵌套地图中选择键?

时间:2016-08-11 10:36:34

标签: clojure

我们说我有一张这样的地图(m):

(def m {:a 1 :b 2 :c {:d 3 :e 4} :e { ... } ....})

我想创建一个仅包含来自:a的{​​{1}},:b:d的新地图,即结果应为:

m

我知道我可以使用{:a 1 :b 2 :d 3} 轻松获取select-keys:a

:b

但是,获得(select-keys m [:a :b]) 的好方法是什么?我正在寻找这样的事情:

:d

Clojure中是否存在这样的功能或推荐的方法是什么?

4 个答案:

答案 0 :(得分:7)

在纯粹的Clojure中,我会这样做:

(defn select-keys* [m paths]
  (into {} (map (fn [p]
                  [(last p) (get-in m p)]))
        paths))

(select-keys* m [[:a] [:b] [:c :d]]) ;;=> {:a 1, :b 2, :d 3}

我更喜欢保持路径的类型是规则的,因此所有路径都有一系列键。在clojure.spec中,这将显示为

(s/def ::nested-map (s/map-of keyword? 
                              (s/or :num number? :map ::nested-map)))
(s/def ::path (s/coll-of keyword?))
(s/fdef select-keys*
        :args (s/cat :m ::nested-map 
                     :paths (s/coll-of ::path)))

答案 1 :(得分:2)

我不知道这样的功能是Clojure的一部分。你可能不得不自己写。我想出了这个:

(defn select-keys* [m v]
  (reduce 
    (fn [aggregate next]
      (let [key-value (if (vector? next)
                        [(last next)
                         (get-in m next)]
                        [next
                         (get m next)])]
        (apply assoc aggregate key-value)))
    {}
    v))

答案 2 :(得分:2)

作为替代方案,您可以对函数使用destruct,例如:

{{1}}

答案 3 :(得分:0)

要求路径为向量,以便您可以使用peek(比last快得多)。减少这样的路径:

(defn select-keys* [m paths] 
  (reduce (fn [r p] (assoc r (peek p) (get-in m p))) {} paths))

(select-keys* m [[:a] [:b] [:c :d]]) ;;=> {:a 1, :b 2, :d 3}

当然,这假设您的所有终端密钥都是唯一的。