如何在Clojure的嵌套映射中选择关键点?

如何在Clojure的嵌套映射中选择关键点?,clojure,Clojure,假设我有一张这样的地图(m): (def m {:a 1 :b 2 :c {:d 3 :e 4} :e { ... } ....}) (select-keys* m [:a :b [:c :d]]) (defn select-keys* [m paths] (into {} (map (fn [p] [(last p) (get-in m p)])) paths)) (select-keys* m [[:a] [:b] [:c :d]

假设我有一张这样的地图(
m
):

(def m {:a 1 :b 2 :c {:d 3 :e 4} :e { ... } ....})
(select-keys* m [:a :b [:c :d]])
(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}
(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}
我想从
m
创建一个只包含
:a
:b
:d
的新地图,即结果应该是:

{:a 1 :b 2 :d 3}
我知道我可以使用
选择键
轻松获取
:a
:b

(select-keys m [:a :b])
但是,还有什么好方法可以同时获得
:d
?我在找这样的东西:

(def m {:a 1 :b 2 :c {:d 3 :e 4} :e { ... } ....})
(select-keys* m [:a :b [:c :d]])
(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}
(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}

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

我不知道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))

也可以对函数使用析构函数,例如:

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

(defn get-m
  [{a :a b :b {d :d} :c}]
  {:a 1 :b b :d d})

(get-m m) ; => {:a 1, :b 2, :d 3}

在纯Clojure中,我会这样做:

(def m {:a 1 :b 2 :c {:d 3 :e 4} :e { ... } ....})
(select-keys* m [:a :b [:c :d]])
(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}
(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}
我更喜欢保持路径的类型是规则的,因此所有路径都有一系列键。在
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)))
你可以用


要求路径为向量,以便可以使用
peek
(比
last
快得多)。通过以下路径减少:

(def m {:a 1 :b 2 :c {:d 3 :e 4} :e { ... } ....})
(select-keys* m [:a :b [:c :d]])
(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}
(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}

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

对于复杂的嵌套数据结构访问,specter@sw1nn之类的东西也会很好地为您服务,这也是我所想的,但您能想出specter解决方案吗?在写问题之前,我确实尝试过specter,但我不知道如何做到这一点。因此,如果你要求你的路径是向量,那么你可以使用
peek
而不是
last
,而且速度更快。