Reputation: 635
I have a nested map like so:
{:A {:B {:A {:B {:A {:B 0}}}}}}
I want to count the occurrences of the pair [:A :B] so that in the case above, the result is 3.
My initial idea was to use clojure.walk/postwalk
to traverse the map and increment a counter.
Is there a more optimal way of achieving this?
Upvotes: 0
Views: 459
Reputation: 17859
tree-seq
works out nice in this case:
(defn count-ab [data]
(->> data
(tree-seq coll? seq)
(keep #(get-in % [:A :B]))
count))
user> (def data {:A {:B {:A {:B {:A {:B 0}}}}}})
#'user/data
user> (count-ab data)
3
user> (def data-1 {:A {:B {:C {:A {:B {:A {:B 0}}}}}}})
#'user/data-1
user> (count-ab data-1)
3
user> (def data-2 {:A {:X {:A {:B 100}}
:B {:C {:A {:B {:A {:B 0}}}}}}})
#'user/data-2
user> (count-ab data-2)
4
Upvotes: 4
Reputation: 715
I would suggest writing a function to turn the deeply nested map into a flat map keyed by paths
(defn flatten-map
"unwind deeply nested map into map of paths and vals"
([m] (into {} (flatten-map [] m)))
([path m]
(if (map? m)
(mapcat (fn [[k v]]
(flatten-map (conj path k) v))
m)
[[path m]])))
you can use this to count the adjacent [:a :b] keys like this
(->> {:A {:B {:A {:B {:A {:B 0}}}}}}
flatten-map
keys
(mapcat #(partition 2 1 %))
(filter #(= [:A :B] %))
count)
Upvotes: 0
Reputation: 1054
Because it's nested map, my pragmatic idea is to traverse recursively and count:
(defn do-count [m]
(loop [m m
counter 0]
(if-let [m* (get-in m [:A :B])]
(recur m* (inc counter))
counter)))
(do-count {:A {:B {:A {:B {:A {:B 0}}}}}}) ;==> 3
Upvotes: 1