我想采取像这样的树状结构:
{"foo" {"bar" "1" "baz" "2"}}
并且在记住来自根的路径时递归遍历以产生类似这样的东西:
["foo/bar/1", "foo/baz/2"]
关于如何在没有拉链或clojure.walk的情况下如何做到这一点的任何建议?
答案 0 :(得分:5)
正如nberger所做的那样,我们将枚举路径与呈现它们作为字符串分开。
<强>枚举强>
功能
(defn paths [x]
(if (map? x)
(mapcat (fn [[k v]] (map #(cons k %) (paths v))) x)
[[x]]))
...返回嵌套地图的路径序列序列。例如,
(paths {"foo" {"bar" "1", "baz" "2"}})
;(("foo" "bar" "1") ("foo" "baz" "2"))
<强>演示强>
功能
#(clojure.string/join \/ %)
...将&#34; /&#34; s连接起来。例如,
(#(clojure.string/join \/ %) (list "foo" "bar" "1"))
;"foo/bar/1"
撰写这些以获得您想要的功能:
(def traverse (comp (partial map #(clojure.string/join \/ %)) paths))
......或者只是
(defn traverse [x]
(->> x
paths
(map #(clojure.string/join \/ %))))
例如,
(traverse {"foo" {"bar" "1", "baz" "2"}})
;("foo/bar/1" "foo/baz/2")
答案 1 :(得分:2)
这是我尝试使用tree-seq
clojure核心功能。
(def tree {"foo" {"bar" "1" "baz" "2"}})
(defn paths [t]
(let [cf (fn [[k v]]
(if (map? v)
(->> v
(map (fn [[kv vv]]
[(str k "/" kv) vv]))
(into {}))
(str k "/" v)))]
(->> t
(tree-seq map? #(map cf %))
(remove map?)
vec)))
(paths tree) ; => ["foo/bar/1" "foo/baz/2"]
地图键用于累积路径。
答案 2 :(得分:1)
我使用累加器快速做了一些事情,但它并不是第一次深度。
(defn paths [separator tree]
(let [finished? (fn [[_ v]] ((complement map?) v))]
(loop [finished-paths nil
path-trees (seq tree)]
(let [new-paths (mapcat
(fn [[path children]]
(map
(fn [[k v]]
(vector (str path separator k) v))
children))
path-trees)
finished (->> (filter finished? new-paths)
(map
(fn [[k v]]
(str k separator v)))
(concat finished-paths))
remaining-paths (remove finished? new-paths)]
(if (seq remaining-paths)
(recur finished remaining-paths)
finished)))))
在repl
中(clojure-scratch.core/paths "/" {"foo" {"bar" {"bosh" "1" "bash" "3"} "baz" "2"}})
=> ("foo/baz/2" "foo/bar/bash/3" "foo/bar/bosh/1")
答案 3 :(得分:1)
以下使用递归深度优先遍历:
(defn combine [k coll]
(mapv #(str k "/" %) coll))
(defn f-map [m]
(into []
(flatten
(mapv (fn [[k v]]
(if (map? v)
(combine k (f-map v))
(str k "/" v)))
m))))
(f-map {"foo" {"bar" "1" "baz" "2"}})
=> ["foo/bar/1" "foo/baz/2"]
答案 4 :(得分:1)
这是我的看法:
(defn traverse [t]
(letfn [(traverse- [path t]
(when (seq t)
(let [[x & xs] (seq t)
[k v] x]
(lazy-cat
(if (map? v)
(traverse- (conj path k) v)
[[(conj path k) v]])
(traverse- path xs)))))]
(traverse- [] t)))
(traverse {"foo" {"bar" "1" "baz" "2"}})
;=> [[["foo" "bar"] "1"] [["foo" "baz"] "2"]]
Traverse返回一个懒的seq路径叶对。然后,您可以将任何转换应用于每个路径叶,例如应用于&#34; / path / to / leaf&#34;完整形式:
(def ->full-path #(->> (apply conj %) (clojure.string/join "/")))
(->> (traverse {"foo" {"bar" "1" "baz" "2"}})
(map ->full-path))
;=> ("foo/bar/1" "foo/baz/2")
(->> (traverse {"foo" {"bar" {"buzz" 4 "fizz" "fuzz"} "baz" "2"} "faa" "fee"})
(map ->full-path))
;=> ("foo/bar/buzz/4" "foo/bar/fizz/fuzz" "foo/baz/2" "faa/fee")