【问题标题】:How to select keys in nested maps in Clojure?如何在 Clojure 的嵌套映射中选择键?
【发布时间】:2016-08-11 10:36:34
【问题描述】:

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

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

我想从m 创建一个只包含:a:b:d 的新地图,即结果应该是:

{:a 1 :b 2 :d 3}

我知道我可以使用select-keys轻松获取:a:b

(select-keys m [:a :b])

但是还有什么好方法可以同时获得:d?我正在寻找这样的东西:

(select-keys* m [:a :b [:c :d]])

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

【问题讨论】:

  • 对于复杂的嵌套数据结构访问,你会得到像幽灵github.com/nathanmarz/specter这样的东西很好的服务
  • @sw1nn 我也是这么想的,但是你能想出幽灵解决方案吗?
  • 我实际上在写问题之前尝试了幽灵,但我不知道如何做到这一点。所以幽灵的答案也将不胜感激:)

标签: clojure


【解决方案1】:

在纯 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)))

【讨论】:

  • 如果你要求你的路径是向量,那么你可以使用peek而不是last,这样会更快。
【解决方案2】:

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

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

【讨论】:

    【解决方案3】:

    您可以使用clojure.walk

    (require '[clojure.walk :as w])
    
    (defn nested-select-keys
      [map keyseq]
      (w/postwalk (fn [x]
                    (if (map? x)
                      (select-keys x keyseq)
                      (identity x))) map))
    
    (nested-select-keys {:a 1 :b {:c 2 :d 3}} [:a :b :c])
      ; => {:a 1, :b {:c 2}}
    

    【讨论】:

      【解决方案4】:

      我不知道这样的函数是 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))
      

      【讨论】:

        【解决方案5】:

        要求路径是向量,以便您可以使用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}
        

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2014-04-30
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多