问题
Given:
{:o {:i1 1
:i2 {:ii1 4}}}
I'd like to iterate over the keys of the map in "absolute" form from the root as a vector. So I'd like:
{
[:o :i1] 1
[:o :i2 :ii1] 4
}
As the result. Basically only get the leaf nodes.
回答1:
A version that I think is rather nicer, using for
instead of mapcat
:
(defn flatten-keys [m]
(if (not (map? m))
{[] m}
(into {}
(for [[k v] m
[ks v'] (flatten-keys v)]
[(cons k ks) v']))))
The function is naturally recursive, and the most convenient base case for a non-map is "this one value, with no keyseq leading to it". For a map, you can just call flatten-keys
on each value in the map, and prepend its key to each keyseq of the resulting map.
回答2:
Looks like this is basically a flatten of the nested keys. This also seems to be a 4clojure problem.
A flatten-map search on github yield many results.
One example implementation:
(defn flatten-map
"Flattens the keys of a nested into a map of depth one but
with the keys turned into vectors (the paths into the original
nested map)."
[s]
(let [combine-map (fn [k s] (for [[x y] s] {[k x] y}))]
(loop [result {}, coll s]
(if (empty? coll)
result
(let [[i j] (first coll)]
(recur (into result (combine-map i j)) (rest coll)))))))
Example
(flatten-map {:OUT
{:x 5
:x/A 21
:x/B 33
:y/A 24}})
=> {[:OUT :x] 5, [:OUT :x/A] 21, [:OUT :x/B] 33, [:OUT :y/A] 24}
An even more general version from Christoph Grand:
(defn flatten-map
"Take a nested map (or a nested collection of key-value pairs) and returns a
sequence of key-value pairs where keys are replaced by the result of calling
(reduce f pk path) where path is the path to this key through the nested
maps."
([f kvs] (flatten-map f nil kvs))
([f pk kvs]
(mapcat (fn [[k v]]
(if (map? v)
(flatten-map f (f pk k) v)
[[(f pk k) v]])) kvs)))
Example:
(flatten-map conj [] {:OUT
{:x 5
:x/A 21
:x/B 33
:y/A 24}})
=> ([[:OUT :x] 5] [[:OUT :x/A] 21] [[:OUT :x/B] 33] [[:OUT :y/A] 24])
来源:https://stackoverflow.com/questions/32853004/iterate-over-all-keys-of-nested-map