在嵌套映射中查找特定键的值
Find Value of Specific Key in Nested Map
在Clojure中,如何找到嵌套映射结构中可能很深的键的值?例如:
(def m {:a {:b "b"
:c "c"
:d {:e "e"
:f "f"}}})
(find-nested m :f)
=> "f"
如果您知道嵌套路径,则使用 get-in。
=> (get-in m [:a :d :f])
=> "f"
详情请看这里:https://clojuredocs.org/clojure.core/get-in
如果您不知道嵌套结构中的路径,您可以编写一个函数,在嵌套映射中递归查找有问题的特定键,并在找到第一个键时 returns 它的值或 returns 序列中 :f 的所有值。
这是一个可以在不知道密钥路径的情况下找到密钥的版本。如果有多个匹配的键,则只返回一个:
(defn find-key [m k]
(loop [m' m]
(when (seq m')
(if-let [v (get m' k)]
v
(recur (reduce merge
(map (fn [[_ v]]
(when (map? v) v))
m')))))))
如果您需要所有值,您可以使用:
(defn merge-map-vals [m]
(reduce (partial merge-with vector)
(map (fn [[_ v]]
(when (map? v) v))
m)))
(defn find-key [m k]
(flatten
(nfirst
(drop-while first
(iterate (fn [[m' acc]]
(if (seq m')
(if-let [v (get m' k)]
[(merge-map-vals m') (conj acc v)]
[(merge-map-vals m') acc])
[nil acc]))
[m []])))))
如果您知道 "path",请考虑使用 get-in:
(get-in m [:a :d :f]) ; => "f"
如果 "path" 未知,您可以使用类似下一个函数的功能:
(defn find-in [m k]
(if (map? m)
(let [v (m k)]
(->> m
vals
(map #(find-in % k)) ; Search in "child" maps
(cons v) ; Add result from current level
(filter (complement nil?))
first))))
(find-in m :f) ; "f"
(find-in m :d) ; {:e "e", :f "f"}
注意:给定的函数只会找到第一个匹配项。
Clojure 提供 tree-seq
对任何值进行深度优先遍历。这将简化查找嵌套键所需的逻辑:
(defn find-nested
[m k]
(->> (tree-seq map? vals m)
(filter map?)
(some k)))
(find-nested {:a {:b {:c 1}, :d 2}} :c)
;; => 1
此外,找到所有匹配项变成了将 some
替换为 keep
的问题:
(defn find-all-nested
[m k]
(->> (tree-seq map? vals m)
(filter map?)
(keep k)))
(find-all-nested {:a {:b {:c 1}, :c 2}} :c)
;; => [2 1]
请注意,具有 nil
值的地图可能需要一些特殊处理。
更新: 如果你看上面的代码,你会发现 k
实际上可以是一个提供更多可能性的函数:
查找字符串键:
(find-nested m #(get % "k"))
查找多个键:
(find-nested m #(some % [:a :b]))
在整数映射中仅查找正值:
(find-nested m #(when (some-> % :k pos?) (:k %)))
在Clojure中,如何找到嵌套映射结构中可能很深的键的值?例如:
(def m {:a {:b "b"
:c "c"
:d {:e "e"
:f "f"}}})
(find-nested m :f)
=> "f"
如果您知道嵌套路径,则使用 get-in。
=> (get-in m [:a :d :f])
=> "f"
详情请看这里:https://clojuredocs.org/clojure.core/get-in
如果您不知道嵌套结构中的路径,您可以编写一个函数,在嵌套映射中递归查找有问题的特定键,并在找到第一个键时 returns 它的值或 returns 序列中 :f 的所有值。
这是一个可以在不知道密钥路径的情况下找到密钥的版本。如果有多个匹配的键,则只返回一个:
(defn find-key [m k]
(loop [m' m]
(when (seq m')
(if-let [v (get m' k)]
v
(recur (reduce merge
(map (fn [[_ v]]
(when (map? v) v))
m')))))))
如果您需要所有值,您可以使用:
(defn merge-map-vals [m]
(reduce (partial merge-with vector)
(map (fn [[_ v]]
(when (map? v) v))
m)))
(defn find-key [m k]
(flatten
(nfirst
(drop-while first
(iterate (fn [[m' acc]]
(if (seq m')
(if-let [v (get m' k)]
[(merge-map-vals m') (conj acc v)]
[(merge-map-vals m') acc])
[nil acc]))
[m []])))))
如果您知道 "path",请考虑使用 get-in:
(get-in m [:a :d :f]) ; => "f"
如果 "path" 未知,您可以使用类似下一个函数的功能:
(defn find-in [m k]
(if (map? m)
(let [v (m k)]
(->> m
vals
(map #(find-in % k)) ; Search in "child" maps
(cons v) ; Add result from current level
(filter (complement nil?))
first))))
(find-in m :f) ; "f"
(find-in m :d) ; {:e "e", :f "f"}
注意:给定的函数只会找到第一个匹配项。
Clojure 提供 tree-seq
对任何值进行深度优先遍历。这将简化查找嵌套键所需的逻辑:
(defn find-nested
[m k]
(->> (tree-seq map? vals m)
(filter map?)
(some k)))
(find-nested {:a {:b {:c 1}, :d 2}} :c)
;; => 1
此外,找到所有匹配项变成了将 some
替换为 keep
的问题:
(defn find-all-nested
[m k]
(->> (tree-seq map? vals m)
(filter map?)
(keep k)))
(find-all-nested {:a {:b {:c 1}, :c 2}} :c)
;; => [2 1]
请注意,具有 nil
值的地图可能需要一些特殊处理。
更新: 如果你看上面的代码,你会发现 k
实际上可以是一个提供更多可能性的函数:
查找字符串键:
(find-nested m #(get % "k"))
查找多个键:
(find-nested m #(some % [:a :b]))
在整数映射中仅查找正值:
(find-nested m #(when (some-> % :k pos?) (:k %)))