Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Merge two lists of maps, combining the maps together on a specific key

Tags:

clojure

I'm running two select statements against Cassandra, so instead of having a join I need to join them in code. Being relatively new to Clojure, I'm having a hard time doing this without resorting to really ugly nested loops. Furthermore, if table-b is missing a matching entry from table-a, it should add default table-b values.

The two selects each result in a list of maps (each "row" is one map). The id key is a UUID, not string.

Here's how the selects look if I def something with the same structure.

(def table-a (list {:id "105421db-eca4-4500-9a2c-08f1e09a35ca" :col-b "b-one"}
                   {:id "768af3f3-3981-4e3f-a93d-9758cd53a056" :col-b "b-two"}))

(def table-b (list {:id "105421db-eca4-4500-9a2c-08f1e09a35ca" :col-c "c-one"}))

I want the end result to be this:

({:id "105421db-eca4-4500-9a2c-08f1e09a35ca" :col-b "b-one" :col-c "c-one"}
 {:id "768af3f3-3981-4e3f-a93d-9758cd53a056" :col-b "b-two" :col-c "default-value"})

Thanks for any help.

like image 786
bigmac Avatar asked Dec 27 '13 21:12

bigmac


People also ask

How do I merge two maps together?

concat() Alternatively, we can use Stream#concat() function to merge the maps together. This function can combine two different streams into one. As shown in the snippet, we are passed the streams of map1 and map2 to the concate() function and then collected the stream of their combined entry elements.

How can I combine two Hashmap objects containing the different types?

Assuming that both maps contain the same set of keys, and that you want to "combine" the values, the thing you would be looking for is a Pair class, see here for example. You simply iterate one of the maps; and retrieve values from both maps; and create a Pair; and push that in your result map.

How do I merge two groovy maps?

The easiest way to merge two maps in Groovy is to use + operator. This method is straightforward - it creates a new map from the left-hand-side and right-hand-side maps.


1 Answers

This can be done by splitting it into groups with the same key, merging all the like-keyed maps and then filling in the default values:

user> (->> (concat table-a table-b)     ;; stat with all the data
           (sort-by :id)                ;; split it into groups
           (partition-by :id)           ;; by id
           (map (partial apply merge))  ;; merge each group into a single map.
           (map #(assoc %               ;; fill in the missing default values.
                    :col-c (or (:col-c %) "default value") 
                    :col-b (or (:col-b %) "default value"))))

({:col-c "c-one", 
  :col-b "b-one", 
  :id "105421db-eca4-4500-9a2c-08f1e09a35ca"} 
 {:col-c "default value", 
  :col-b "b-two", 
  :id "768af3f3-3981-4e3f-a93d-9758cd53a056"})

Using the thread-last macro ->> makes this a lot easier for me to read, though that is just my opinion. There is also likely a more elegant way to supply the default keys.

like image 163
Arthur Ulfeldt Avatar answered Sep 19 '22 01:09

Arthur Ulfeldt