Lēctia Landau
Lēctia Landau

Reputation: 573

How to nest a sequence of layered steps?

Not really sure what to call this, what I mean is I have a list of steps e.g.

[{:id 1 :layer :foo}
 {:id 2 :layer :foo/bar}
 {:id 3 :layer :foo}
 {:id 4 :layer :bar/baz}
 {:id 5 :layer :foo/bar}
 {:id 6 :layer :foo/baz}
 {:id 7 :layer :foo/baz}
 {:id 8 :layer :foo/baz}
 {:id 9 :layer :foo/baz}
 {:id 10 :layer :foo}]

And I want to write a function that would return a nested sequence like so:

[{:layer :foo :steps [1 {:layer :foo/bar :steps [2]} 3]}
 {:layer :bar :steps [{:layer :bar/baz :steps [4]}]}
 {:layer :foo :steps [{:layer :foo/bar :steps [5]}
                      {:layer :foo/baz :steps [6 7 8 9]}
                      10]}]

Conceptually I understand how zippers might be used for something like this, but I don't know how to use them well enough to apply even after research. This is similar to what I'd use postwalk for, except there's a lot of next step previous step stuff regarding when to go up or down a layer.

Is there any alternative that's more idiomatic than a big ugly loop?

Upvotes: 0

Views: 186

Answers (1)

Martin Půda
Martin Půda

Reputation: 7568

If you changed your layers into vectors of keywords...

[{:id 1 :layer [:foo]}
 {:id 2 :layer [:foo :bar]}
 {:id 3 :layer [:foo]}
 {:id 4 :layer [:bar :baz]}
 {:id 5 :layer [:foo :bar]}
 {:id 6 :layer [:foo :baz]}
 {:id 7 :layer [:foo :baz]}
 {:id 8 :layer [:foo :baz]}
 {:id 9 :layer [:foo :baz]}
 {:id 10 :layer [:foo]}]

... you could use something like this:

(defn solution
  ([object] (solution object 0 []))
  ([object level path-so-far]
   (into [] (mapcat (fn [part]
                      (if-let [delimiter (-> part first :layer (nth level nil))]
                        [{:layer (conj path-so-far delimiter)
                          :steps (solution part
                                           (inc level)
                                           (conj path-so-far delimiter))}]
                        (mapv :id part))))
         (partition-by (comp #(nth % level nil) :layer) object))))

With this result:

(solution [{:id 1 :layer [:foo]}
           {:id 2 :layer [:foo :bar]}
           {:id 3 :layer [:foo]}
           {:id 4 :layer [:bar :baz]}
           {:id 5 :layer [:foo :bar]}
           {:id 6 :layer [:foo :baz]}
           {:id 7 :layer [:foo :baz]}
           {:id 8 :layer [:foo :baz]}
           {:id 9 :layer [:foo :baz]}
           {:id 10 :layer [:foo]}])
=>
[{:layer [:foo], :steps [1 {:layer [:foo :bar], :steps [2]} 3]}
 {:layer [:bar], :steps [{:layer [:bar :baz], :steps [4]}]}
 {:layer [:foo], :steps [{:layer [:foo :bar], :steps [5]} {:layer [:foo :baz], :steps [6 7 8 9]} 10]}]

Upvotes: 2

Related Questions