Clojure - Recursively Semi-Flatten Nested Map
在Clojure中,如何旋转这样的嵌套地图:
1 2 3 4 5 6 7 8 9 10 | (def parent {:id"parent-1" :value"Hi dude!" :children [{:id"child-11" :value"How is life?" :children [{:id"child-111" :value"Some value" :children []}]} {:id"child-12" :value"Does it work?" :children []}]}) |
此:
1 2 3 4 5 6 | [ [{:id"parent-1", :value"Hi dude!"}] [{:id"parent-1", :value"Hi dude!"} {:id"child-11", :value"How is life?"}] [{:id"parent-1", :value"Hi dude!"} {:id"child-11", :value"How is life?"} {:id"child-111", :value"Some value"}] [{:id"parent-1", :value"Hi dude!"} {:id"child-12", :value"Does it work?"}] ] |
我正在尝试非常棘手的递归尝试,现在我的大脑已经筋疲力尽了。
到目前为止,我所掌握的是下面的内容。它确实获取了正确的数据,但是却将数据放入了一些多余的嵌套向量中。
该如何解决?
在Clojure中是否有一种很好的惯用方式来做到这一点?
谢谢。
1 2 3 4 5 6 7 | (defn do-flatten [node parent-tree] (let [node-res (conj parent-tree (dissoc node :children)) child-res (mapv #(do-flatten % node-res) (:children node)) end-res (if (empty? child-res) [node-res] [node-res child-res])] end-res)) (do-flatten parent []) |
哪个生产:
1 2 3 4 5 6 7 8 9 10 11 | [ [{:id"parent-1", :value"Hi dude!"}] [[ [{:id"parent-1", :value"Hi dude!"} {:id"child-11", :value"How is life?"}] [[ [{:id"parent-1", :value"Hi dude!"} {:id"child-11", :value"How is life?"} {:id"child-111", :value"Some value"}] ]]] [ [{:id"parent-1", :value"Hi dude!"} {:id"child-12", :value"Does it work?"}] ]] ] |
我不知道这是否是惯用的,但似乎可行。
1 2 3 4 5 6 7 | (defn do-flatten ([node] (do-flatten node [])) ([node parents] (let [path (conj parents (dissoc node :children))] (vec (concat [path] (mapcat #(do-flatten % path) (:children node))))))) |
您可以在调用
我倾向于使用一些局部状态来简化逻辑:
1 2 3 4 5 6 7 8 9 10 | (defn do-flatten ([node] (let [acc (atom [])] (do-flatten node [] acc) @acc)) ([node base acc] (let [new-base (into base (self node))] (swap! acc conj new-base) (doall (map #(do-flatten % new-base acc) (:children node)))))) |
也许某些功能纯粹主义者会不喜欢它,当然,您可以用纯粹的功能方式完成整个事情。我的感觉是,这是一个临时的,完全是局部的状态(因此不会引起该州臭名昭著的各种问题),所以如果它可以提高可读性(我认为确实如此),我很高兴使用它。
另一个选择是使用拉链:
1 2 3 4 5 6 7 8 9 10 11 | (require '[clojure.zip :as z]) (defn paths [p] (loop [curr (z/zipper map? :children nil p) res []] (cond (z/end? curr) res (z/branch? curr) (recur (z/next curr) (conj res (mapv #(select-keys % [:id :value]) (conj (z/path curr) (z/node curr))))) :else (recur (z/next curr) res)))) |