1
votes

I need to implement a function taking two vectors of maps and return a kind of combined one vector of maps. Details are below:

input 1: [{:id 1 :car "A" :price 10}{:id 2 :car "B" :price 20}{:id 3 :car "C" :price 30}]

input 2: [{:id 4 :car "A" :price 5}{:id 5 :car "B" :price 30}{:id 6 :car "D" :price 40}]

output: [{:id 4 :car "A" :price 5} {:id 2 :car "B" :price 20} {:id 3 :car "C" :price 30} {:id 6 :car "D" :price 40}]

That is, pick up the minimum value of price if :car are the same, or directly add to output with :id.

I have considered using map to get each value to compare within a nested loop but I believe that is not a nice way to do it. Then I learn something like clojure.walk and juxt, but they seem like quite fancy and need more explanations.

The other possible abstract solution I think is to concat them together, and check though each map in vector using flag to check price. Pick up the minimun and remove the larger one.

I hope you can help me and thank you so much!

1

1 Answers

4
votes

one way to do this would is to group items by :car and then to find the value with minimum price for every group:

user> (->> (concat data1 data2)
           (group-by :car)
           vals
           (map #(apply min-key :price %)))

;;=>({:id 4, :car "A", :price 5} {:id 2, :car "B", :price 20} {:id 3, :car "C", :price 30} {:id 6, :car "D", :price 40})

you can also do it in one pass with reduce, which is a bit more verbose, but should have better performance:

(defn process [& colls]
  (vals (reduce (fn [acc {car :car :as item}]
                  (if (acc car)
                    (update acc car (partial min-key :price) item)
                    (assoc acc car item)))
                {}
                (apply concat colls))))

user> (process data1 data2)
;;=> ({:id 4, :car "A", :price 5} {:id 2, :car "B", :price 20} {:id 3, :car "C", :price 30} {:id 6, :car "D", :price 40})