6
votes

I have two arrays of maps

1st is [{:a 1 :b 2 :d 6} {:a 2 :b 2} {:a 7 :b 7}]

2nd is [{:a 3 :c 3 :e 9 :y 7} {:a 2 :b 6 :c 8}]

depending on the value of a i.e. if its matches in 2nd array the '2nd map' should be merged with '1st map' and the resultant array of maps should be

Res should be [{:a 1 :b 2 :d 6} {:a 2 :b 6 :c 8} {:a 7 :b 7} {:a 3 :c 3 :e 9 :y 7}]

Can anyone help me on this. Thanks in advance.

3
I'm not quite sure what the algorithm is, in other words, I'm not quite sure what you're comparing to get the "Res".octopusgrabbus
i am comparing value of key 'a'. if the val of 'a' from 1st map matches the value of 'a' in any of the maps in 2nd array perform this (merge {1st map having 'a' value = 1} {2nd map having 'a' value = 1 }) and add this result map to the final mapAvi

3 Answers

6
votes

Here you go:

user> (def xs [{:a 1 :b 2 :d 6} {:a 2 :b 2} {:a 7 :b 7}])
#'user/xs
user> (def ys  [{:a 3 :c 3 :e 9 :y 7} {:a 2 :b 6 :c 8}])
#'user/ys
user> (for [[a ms] (group-by :a (concat xs ys))] (apply merge ms))
({:a 1, :b 2, :d 6} {:a 2, :c 8, :b 6} {:a 7, :b 7} {:y 7, :a 3, :c 3, :e 9})
4
votes

This data structure looks very unwieldy to me nevertheless here's my take:

(defn key-by-a [coll]
  "Convert a list of maps to a map of maps keyed by their vals at :a"
  (apply hash-map (mapcat (juxt :a identity) coll)))

(defn merge-map-lists [l1 l2]
  (->> [l1 l2]
    (map key-by-a)
    (apply merge-with merge)
    (vals)))

One thing it doesn't do is maintaining order of the input lists but since it is not clear which list decides (both might have same keys in different orders) I left that out.

0
votes

maybe clojure.set/join is what you want:

here is the docs of clojure.set/join:

user=> (def animals #{{:name "betsy" :owner "brian" :kind "cow"}
                      {:name "jake"  :owner "brian" :kind "horse"}
                      {:name "josie" :owner "dawn"  :kind "cow"}})

user=> (def personalities #{{:kind "cow" :personality "stoic"}
                            {:kind "horse" :personality "skittish"}})
#'user/personalities
user=> (join animals personalities)

#{{:owner "dawn",  :name "josie", :kind "cow",   :personality "stoic"}
  {:owner "brian", :name "betsy", :kind "cow",   :personality "stoic"}
  {:owner "brian", :name "jake",  :kind "horse", :personality "skittish"}}

user=> (join animals personalities)
#{{:kind "horse", :owner "brian", :name "jake", :species "cow", :personality "stoic"}
  {:kind "cow", :owner "dawn", :name "josie", :species "cow", :personality "stoic"}
  {:kind "horse", :owner "brian", :name "jake", :species "horse", :personality "skittish"}
  {:kind "cow", :owner "brian", :name "betsy", :species "cow", :personality "stoic"}
  {:kind "cow", :owner "dawn", :name "josie", :species "horse", :personality "skittish"}
  {:kind "cow", :owner "brian", :name "betsy", :species "horse", :personality "skittish"}}


;; Notice that "Jake" is both a horse and a cow in the first line. That's 
;; likely not what you want. You can tell `join` to only produce output 
;; where the `:kind` value is the same as the `:species` value like this:

user=> (join animals personalities {:kind :species})
#{{:kind "cow", :owner "dawn", :name "josie", :species "cow", :personality "stoic"}
  {:kind "horse", :owner "brian", :name "jake", :species "horse", :personality "skittish"}
  {:kind "cow", :owner "brian", :name "betsy", :species "cow", :personality "stoic"}}