A program that I work on constantly updates and modifies program state containing an arbitrarily deep and wide tree. The data looks something like this.
(defonce state
(r/atom {:some "program"
:state "here"
:tree [{:topic "Books"
:expanded true
:children [{:topic "Titles"}
{:topic "Authors"
:expanded true
:children [{:topic "Alice"}
{:topic "Bob"}
{:topic "Carol"}]}
{:topic "Genres"}]}
{:topic "CDs"
:children [{:topic "Genres"}
{:topic "Albums"}
{:topic "Artists"}]}
{:topic "To Do"
:expanded true
:children [{:topic "Spouse Birthday"
:expanded nil
:due-date "07/31/2025"
:children [{:topic "Buy Card"}
{:topic "Buy Jewelry"}
{:topic "Buy Cake"}]}]}]}))
As the program runs, I need to extract the series of numeric vector indices to reach the visible topics (those that have :children
where the :expanded
flag is present and "truthy").
Here is a method that works.
;; This flattening function is from an idea presented in response to
;; this question:
;; https://stackoverflow.com/questions/5232350/clojure-semi-flattening-a-nested-sequence
(defn flatten-to-vectors
[s]
(mapcat #(if (every? coll? %) (flatten-to-vectors %) (list %)) s))
(defn visible-nodes
[tree so-far]
(flatten-to-vectors
(map-indexed
(fn [idx ele]
(let [new-id (conj so-far idx)]
(if (not (and (:children ele) (:expanded ele)))
new-id
(cons new-id (visible-nodes (:children ele) new-id)))))
tree)))
;; The function can be called like this on the data above.
(println (visible-nodes (:tree @state) []))
;; => ([0] [0 0] [0 1] [0 1 0] [0 1 1] [0 1 2] [0 2] [1] [2] [2 0])
Although this works, it seems like I should be able to generate the correct result without creating nested sequences that require flattening. But I just can't come up with anything.
Any suggestions would be appreciated.