如何在Clojure的嵌套数组映射中找到键的路径?

THX*_*137 3 clojure clojurescript

假设我有:

(def a-map {:foo "bar" :biz {:baz "qux"}})
Run Code Online (Sandbox Code Playgroud)

我如何找到给定值“qux”的键路径,使得

(get-in a-map <the resulting path>) 
Run Code Online (Sandbox Code Playgroud)

会返回“qux”吗?

换句话说,一个接受 a-map 和 "qux" 并返回 [:biz :baz] 的函数。

然后我就可以像这样使用返回的路径:

 (get-in a-map [:biz :baz])
Run Code Online (Sandbox Code Playgroud)

并得到“qux”。

我需要的路径将比这个简单的例子嵌套得多。

我想在 html 中找到给定值的路径,该值已被解析为使用山核桃的数组映射。我想要做到这一点,而不必尝试在数十个嵌套的键/值中进行心理导航。我对其他策略持开放态度。

lee*_*ski 9

您可以为此使用拉链:像这样,例如:

user> (require '[clojure.zip :as z])
nil

user> 
(loop [curr (z/zipper coll? seq nil a-map)]
  (cond (z/end? curr) nil
        (-> curr z/node (= "qux")) (->> curr
                                        z/path
                                        (filter map-entry?)
                                        (mapv first))
        :else (recur (z/next curr))))
;;=> [:biz :baz]
Run Code Online (Sandbox Code Playgroud)

或相同,但采用更“声明式”的风格:

(some->> a-map
         (z/zipper coll? seq nil)
         (iterate z/next)
         (take-while (complement z/end?))
         (filter #(= (z/node %) "qux"))
         first
         z/path
         (filter map-entry?)
         (mapv first))
Run Code Online (Sandbox Code Playgroud)

更新

您还可以使用经典的递归方法:

(defn get-path [endpoint data]
  (cond (= endpoint data) []
        (map? data) (some (fn [[k v]]
                            (when-let [p (get-path endpoint v)]
                              (cons k p)))
                          data)))

user> (get-path "qux" a-map)
;;=> (:biz :baz)
Run Code Online (Sandbox Code Playgroud)

  • 我以为你可能有。但*键的路径*不要求是向量。相关的标准函数“get-in”和“assoc-in”接受任何按键序列。我承认这个结构不太可能很深,而且深度当然不能超过递归的限制。 (3认同)
  • @缩略图,确实如此。为了可读性,我有意识地这样做了。(以避免列表-&gt;向量转换的多一级间接) (2认同)
  • 哇。这些年来我*绝对确定*他们只接受向量。显然应该是“(cons kp)”!更新。 (2认同)