Search code examples
clojureclojurescript

Clojure: How to merge vector of maps on the basis of index?


I am trying to merge vector of maps.

I tried doing it using the reduce method but unable to retrieve the expected result.

(def data '([{:padding-top "30px"} {:padding-top "40px"} {:padding-top "50px"}] [{:margin "40px"}]))

(reduce #(hash-map %1 %2) () data)

Input data:

(def data '([{:padding-top "30px"} {:padding-top "40px"} {:padding-top "50px"}] [{:margin "40px"}]))

(defn merge-data
  [data]
)

Expected Output:

(merge-data data)

({:padding-top "30px" :margin "40px"}
  {:padding-top "40px"}
  {:padding-top "50px"})

Coming from the JS background, I can easily do it using something like forEach and conditionals to build expected output. But how to do it in functional way?

SOLUTION:

I was able to solve this problem in the following way

(defn merge-styles
  [& args]
  (let [max-count (apply max (map #(count %1) args))
        items (map #(take max-count (concat %1 (repeat nil))) args)]
    (apply map merge items)))

The code snippet makes it much clearer and leaner. Thanks a lot for all the answers which helped me get up to this point.


Solution

  • Generally, you can just use map and merge to merge collections of hashmaps, but it will end merging when one of the collections is exhausted.

    You can create a function like the following to "extend" the collections to have the same length, then merge as usual:

    (defn merge-all
      "Merges two sequences of maps using merge. Consumes all entries."
      [xs ys]
      (let [n  (max (count xs) (count ys))
            xs (take n (concat xs (repeat nil)))
            ys (take n (concat ys (repeat nil)))]
        (map merge xs ys)))
    
    (def data [[{:padding-top "30px"} {:padding-top "40px"} {:padding-top "50px"}]
               [{:margin "40px"}]])
    
    ;; (apply merge-all data)
    ;; => ({:padding-top "30px", :margin "40px"} {:padding-top "40px"} {:padding-top "50px"})
    

    Note that in your example, you used a parenthesis around the data, but in Clojure this means you want to call it as if it were a function. In the example above I switched it to a [ and ] instead. Also, note that this function depends in the fact that you can actually count the collections that you pass to it (in Clojure you can have "infinite" collections, such as (range)).