Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Clojure - Recursively Flatten Nested Maps

Given a map with the key :content, where content is a list of strings or other maps, how can I flatten out the values to receive only the strings?

(flattener {:content '("b" {:content ("c" {:content ("d")})} "e")})

> '("b" "c" "d" "e")

I'm stumbling through very hacky loop recur attempts and now my brain is burnt out. Is there a nice idiomatic way to do this in Clojure?

Thanks.

What I've got is below, and although it works, it's quite ugly

(defn flatten-content
  [coll]
  (loop [acc '(), l coll]
    (let [fst (first l), rst (rest l)]
      (cond
       (empty? l) (reverse acc)
       (seq? fst) (recur acc (concat fst rst))
       (associative? fst) (recur acc (concat (:content fst) rst))
       :else (recur (conj acc fst) rst)))))
like image 453
Scott Klarenbach Avatar asked Nov 29 '14 23:11

Scott Klarenbach


1 Answers

The tree-seq function helps walk, and since your map

(def m {:content '("b" {:content ("c" {:content ("d")})} "e")})

always has a list of "children" keyed by :content, this works

(filter string? (tree-seq associative? :content m))
;=> ("b" "c" "d" "e")
like image 88
Mike Fikes Avatar answered Sep 26 '22 12:09

Mike Fikes