How would one elegantly take these two inputs:
(def foo [:a 1 :a 1 :a 2])
(def bar [{:hi "there 1"}{:hi "there 2"}{:hi "there 3"}{:hi "there 4"}{:h1 "there 5"}])
and get:
[{:hi "there 1" :a 1}{:hi "there 2" :a 1}{:hi "there 3" :a 2}{:hi "there 4" :a 1}{:hi "there 5" :a 1}]
The first collection cycles at the point the second collection reaches the same number of elements. It would be fine for the first collection to be any of these as it's going to be hard coded:
(def foo [{:a 1} {:a 1} {:a 2}])
(def foo [[:a 1] [:a 1] [:a 2]])
(def foo [1 1 2])
There may be another data structure that would be even better. The 1 1 2
is deliberate as it's not 1 2 3
which would allow range
or something like that.
Cycling through the first collection is easy... I'm not sure how to advance through the second collection at the same time. But my approach may not be right in the first place.
As usual, I tend toward weird nested imitations of imperative code but I know there's a better way!
Here's one way to do it:
You can take the values from foo
, cycle through them and partition them in groups of 2 at a time. There's a little secret of vectors of size 2, which is that they can work as a little map (1 key/value pair).
Once we have two collections of maps, we can merge them together. One collection is infinite but that's OK, map
will compute only the values until one collection runs out of elements. mapv
is the same as map
but it returns a vector instead.
(def foo [:a 1 :a 1 :a 2])
(def bar [{:hi "there 1"}{:hi "there 2"}{:hi "there 3"}{:hi "there 4"}{:h1 "there 5"}])
(defn cycle-and-zip [xs maps]
(let [xs-pairs (->> xs cycle (partition 2) (map vec))]
(mapv merge maps xs-pairs)))
(cycle-and-zip foo bar)
;; => [{:hi "there 1", :a 1} {:hi "there 2", :a 1} {:hi "there 3", :a 2} {:hi "there 4", :a 1} {:h1 "there 5", :a 1}]
Update: replaced map
with mapv
so the output is actually a vector.