2
votes

I'm working on some Clojure code, in which I have a tree of entities like this:

foo1
 +-- bar1
 | +-- baz1
 | +-- baz2
 +-- bar2
   +-- baz3
foo2
 +-- bar3
   +-- baz4

In case my absurd ASCII art doesn't make sense, I have a list of foos, each of which can have zero or more bars, each of which may have zero or more bazes.

What I am trying to do is generate a hash map where the keys are baz IDs and the values are bar IDs; i.e. the above diagram would be:

{"baz1" "bar1", "baz2" "bar1", "baz3" "bar2", "baz4" "bar3"}

My data structures look like this:

(def foos [
  {:id "foo1" :bars [
    {:id "bar1" :bazes [
      {:id "baz1"}
      {:id "baz2"}
    ]}
    {:id "bar2" :bazes [
      {:id "baz3"}
    ]}
  ]}
  {:id "foo2" :bars [
    {:id "bar3" :bazes [
      {:id "baz4"}
    ]}
  ]}
])

And here is the code that I have that builds the baz-to-bar map:

(defn- baz-to-bar [foos]
  (let [b2b-list (flatten (for [f foos] (flatten (for [bar (:bars c)] (flatten (for [baz (:bazes bar)] [(:id baz) (:id bar)]))))))
        b2b-map (if (not (empty? b2b-list)) (apply hash-map b2b-list))]
    (if b2b-map [:b2b (for [baz (keys b2b-map)] (entry-tag baz (b2b-map baz)))])))

It works, but is pretty obtuse.

Can anyone suggest a more elegant, hopefully idiomatic way to do this in Clojure?

1

1 Answers

6
votes
(into {} (for [foo foos
               {bar-id :id :as bar} (:bars foo)
               {baz-id :id} (:bazes bar)]
           {baz-id bar-id}))