Could anyone help me make sure what I've done is idiomatic Clojure? I'm experienced in functional programming but not so much idiomatic Clojure style. I have a function that turns the output of a SQL query into a message board data structure (it's literally a tree). It works perfectly well, but I'd like to learn how to do it in a more Clojurian way. Snippet is here, along with example thread: https://pastebin.com/Xywtmw4z
@surgo I would be tempted to use a pair of hash-maps, one to build an adjacency list representation (key is comment id, val is direct children in a vector), and one to map from id to the full comment data, then build the tree from the leaves up - I suspect that would lead to smaller code. But nothing I see there is un-idiomatic for clojure
Alright, thanks. Mostly I was concerned by the amount of my (recur ...) usage. I kept thinking, "surely there's a function like map / reduce to do this"
actually it might be the opposite for the adjacency list - child to parent, in order to build bottom up
the sign there would be if you are consuming a sequence one end to the other
that is, for being able to use a clojure higher order function, you want sequential input consumption, which I don't think fits your code
ah yes, makes sense
I mean, I'm consuming the sql results one end to the other buuut the structure it's forming isn't quite as simple
right - my suggestion would start with something a lot like (let [comments (group-by :id sql-results) parents (reduce (fn [m child] (update m (:id child) (fnil conj []) (:parent child))) {} sql-results)] ...)
(clearly I am too tired to be doing this, so many edits, heh
)
well, I see where you're going with it anyway
Thanks
You know, it occurs to me that I can simplify it a lot more than that due to the fact that sql results already comes both in order and with depth information. Why I didn't realize that the first time around is beyond me.
oh yeah - if you keep a hash-map tracking where to find each parent, each child is guaranteed to come after the parent and can be attached via an assoc-in
call on an accumulator in a reduce
the acc in the reduce would be something like {:node-paths {} :tree {}}
where node-paths map from an existing id, to the vector you would pass to update-in to find that item in the tree
so inserting would involve said update in, plus appending a new id at the end of a new entry in the node-paths
anyway, there is an idiom here (which is also good programming practice) of not doing multiple linear searches on a sequential input, and instead leveraging associative data structures
indeed