Clojure - подсчитывать вхождения вложенного ключа во вложенную карту?
У меня есть вложенная карта примерно так:
{:A {:B {:A {:B {:A {:B 0}}}}}}
Я хочу посчитать вхождения пары [:A:B], чтобы в приведенном выше случае результат составил 3.
Моей первоначальной идеей было использовать clojure.walk/postwalk
пройти карту и увеличить счетчик.
Есть ли более оптимальный способ достижения этого?
3 ответа
Решение
tree-seq
хорошо работает в этом случае:
(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
Поскольку это вложенная карта, моя прагматичная идея состоит в том, чтобы проходить рекурсивно и считать:
(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
Я бы предложил написать функцию, чтобы превратить глубоко вложенную карту в плоскую карту с указанием путей
(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]])))
Вы можете использовать это для подсчета соседних клавиш [:a:b], как это
(->> {:A {:B {:A {:B {:A {:B 0}}}}}}
flatten-map
keys
(mapcat #(partition 2 1 %))
(filter #(= [:A :B] %))
count)