You can write a recursive convolution function as a function of a higher order, for example:
(defn rollup ([data heirarchy func] (loop [top (second (first heirarchy))] (if (nil? (heirarchy top)) (rollup data heirarchy func top) (recur (heirarchy top))))) ([data heirarchy func root] (let [children (reduce (fn [l [kv]] (if (= v root) (cons kl) l)) '() heirarchy) data (reduce (fn [dc] (if (dc) d (rollup d heirarchy func c))) data children) child-values (map data children)] (assoc data root (apply func child-values)))))
which can then be used with any particular drilling operation or hierarchy you like:
(def populations { :africa/liberia 3.4e6 :africa/ethiopia 7.4e7}) (def geography {:africa/liberia :africa :africa/ethiopia :africa :africa :world}) (rollup populations geography +) => {:africa 7.74E7, :world 7.74E7, :africa/ethiopia 7.4E7, :africa/liberia 3400000.0}
Obviously, it becomes more complex if you have very large data sets or several hierarchies, etc., but this should be enough for many simple cases.
source share