如何在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
,而且速度更快。