I'd like to merge two lists by some map key as follow:
(def list1 '({:a 2 :b 2} {:a 1 :b 1}))
(def list2 '({:a 1 :c 1} {:a 2 :c 2}))
As result I'd like something like, using sort by :a for example:
'({:a 1 :b 1 :c 1} {:a 2 :b 2 :c 2})
Any ideas?
You can use join and sort-by:
(:require '[clojure.set :as s])
(sort-by :a (s/join list1 list2 {:a :a}))
Does this do it?
(def list1 '({:a 1 :b 1} {:a 2 :b 2}))
(def list2 '({:a 1 :c 1} {:a 2 :c 2}))
(println
(map merge list1 list2)
)
;=> ({:a 1, :b 1, :c 1} {:a 2, :b 2, :c 2})
UPDATE
(def list1 [ {:a 1 :b 1} {:a 2 :b 2} ] )
(def list2 [ {:a 2 :c 2} {:a 1 :c 1} ] )
(defn sort-merge [lista listb]
(map merge (sort-by :a lista) (sort-by :a listb)))
(println
(sort-merge list1 list2))
;=> ({:a 1, :b 1, :c 1} {:a 2, :b 2, :c 2})
another way is to use list comprehension:
user> (for [x list1
y list2
:when (= (:a x) (:a y))]
(merge x y))
({:a 2, :b 2, :c 2} {:a 1, :b 1, :c 1})
Related
Say I have two maps in clojure.
(def map1 {:a 1 :b 1 :c nil :d 1})
(def map2 {:a 1 :b 2 :c 3 :d nil})
(def listofmaps '({:a 1 :b 1 :c nil :d 1} {:a 2 :b 2 :c 2 :d nil}))
If :a value matches with any map in listofmaps and map1, then if map1 :d is not null, put :d value from map1 into the matching map in listofmaps.
Like, first we compare map1 and listofmaps - now if map1 (:a 1) matches with any maps in listofmaps (:a 1), if map1 (:d not null) replace (matching map in listofmaps with map1 :d value) and if map1 (:c not null) replace (matching map in listofmaps with map1 :c value)
(def map1 {:a 1 :b 1 :c nil :d 1})
(def listofmaps '({:a 1 :b 1 :c nil :d 1} {:a 2 :b 2 :c 2 :d nil}))
Then map2 (:a 1) matches with a map in listofmaps (:a 1) and :
(def map2 {:a 1 :b 2 :c 3 :d nil})
(def listofmaps '({:a 1 :b 1 :c nil :d 1} {:a 2 :b 2 :c 2 :d nil}))
if map2 (:d not null) replace (matching map in listofmaps with map2 :d value) and if map2 (:c not null) replace (matching map in listofmaps with map2 :c value)
output=> '({:a 1 :b 1 :c 3 :d 1} {:a 2 :b 2 :c 2 :d nil})
it's not clear what is meant by in list of maps and map2, here is a reasonably common pattern of adding in missing values in priority order.
(let [map1 {:a 1 :b 1 :c nil :d 1}
map2 {:a 1 :b 2 :c 3 :d nil}
list-of-maps [{:a 1 :b 1 :c nil :d 1} {:a 2 :b 2 :c 2 :d nil}]
or-fn (fn [a b] (or a b))]
(->>
list-of-maps
(map #(merge-with or-fn % map1))
(map #(merge-with or-fn % map2))))
({:a 1, :b 1, :c 3, :d 1} {:a 2, :b 2, :c 2, :d 1})
I understood your question to be
If the value of the :a key in the new map matches the value of the :a key in any map in listofmaps, then, for each such matched map, replace the values of the keys :c and :d in that matched map in listofmaps by a new value only if the corresponding new value is not null.
Assuming that, here is an answer.
If I did not understand the question correctly, please clarify and show your desired output.
user> (def map1 {:a 1 :b 1 :c nil :d 1})
#'user/map1
user> (def map2 {:a 1 :b 2 :c 3 :d nil})
#'user/map2
user> (def listofmaps [{:a 1 :b 1 :c nil :d 1} {:a 2 :b 2 :c 2 :d nil}])
#'user/listofmaps
user> (defn mapper [m ms]
(mapv (fn [elem]
(if (= (:a elem) (:a m))
(merge-with #(or %1 %2) (select-keys m [:c :d]) elem)
elem))
maps))
#'user/mapper
user> (mapper map1 listofmaps)
[{:c nil, :d 1, :a 1, :b 1} {:a 2, :b 2, :c 2, :d nil}]
user> (mapper map2 listofmaps)
[{:c 3, :d 1, :a 1, :b 1} {:a 2, :b 2, :c 2, :d nil}]
user>
If our input were to look something like this:
(({:a 1 :b 100} {:a 2 :b 300} {:a 4 :b 0}) ({:a 0 :b 10} {:a 4 :b 50}))
Our range that we would like to police over would be (0 1 2 3 4)
We would like the output to be:
(({:a 0 :b 0} {:a 1 :b 100} {:a 2 :b 300} {:a 3 :b 0} {:a 4 :b 0})
({:a 0 :b 10} {:a 1 :b 0} {:a 2 :b 0} {:a 3 :b 0} {:a 4 :b 50}))
Basically what it should do is look at the first list of maps then the second and so on and figure out what the range is for :a. We can easily do that with a min/max function. Now it creates a range and applies it to both lists. If an :a is missing on one list it adds in that :a with a :b of 0. (ie the addition of {:a 0 :b 0} or {:a 3 :b 0} in the first list. We have a function that can somewhat do it, but aren't quite there yet. Here it is:
(map
#(doseq [i (vec myRange)]
(if (some (fn [list] (= i list)) (map :a %))
nil
(println (conj % {:a i :b 0}))))
myList)
Obviously because of Clojures immutable data structures this function fails. If our input is something like:
(({:a 1, :b 1} {:a 2, :b 3} {:a 4, :b 5})
({:a 0, :b 3} {:a 4, :b 1}))
our output is:
(nil nil)
but if we println:
({:a 0, :b 0} {:a 1, :b 1} {:a 2, :b 3} {:a 4, :b 5})
({:a 3, :b 0} {:a 1, :b 1} {:a 2, :b 3} {:a 4, :b 5})
({:a 1, :b 0} {:a 0, :b 3} {:a 4, :b 1})
({:a 2, :b 0} {:a 0, :b 3} {:a 4, :b 1})
({:a 3, :b 0} {:a 0, :b 3} {:a 4, :b 1})
(nil nil)
We want the output to look like:
(({:a 0, :b 0} {:a 1, :b 1} {:a 2, :b 3} {:a 3, :b 0} {:a 4, :b 5})
({:a 0, :b 3} {:a 1, :b 0} {:a 2, :b 0} {:a 3, :b 0} {:a 4, :b 1}))
without the use of a println. Any suggestions?
the idea of working with immutable data in loop, is to pass the result of the latest iteration to the next one. You could do it with loop/recur, but in your case it is common to use reduce function (which is literally one of the cornerstones of functional programming):
(defn update-coll [range items]
(reduce (fn [items i] (if (some #(= (:a %) i) items)
items
(conj items {:a i :b 0})))
items range))
the first parameter to reduce "updates" items for every value of range (i), passing the updated value to the next iteration.
now you just have to map your input data with it:
(def input '(({:a 1 :b 100} {:a 2 :b 300} {:a 4 :b 0})
({:a 0 :b 10} {:a 4 :b 50})))
(map (comp (partial sort-by :a)
(partial update-coll [0 1 2 3 4]))
input)
output:
(({:a 0, :b 0} {:a 1, :b 100} {:a 2, :b 300}
{:a 3, :b 0} {:a 4, :b 0})
({:a 0, :b 10} {:a 1, :b 0} {:a 2, :b 0}
{:a 3, :b 0} {:a 4, :b 50}))
also you can do it without accumulation using clojure's sets:
(defn process-input [input r]
(let [r (map #(hash-map :a % :b 0) r)]
(map (fn [items] (into (apply sorted-set-by
#(compare (:a %1) (:a %2))
items)
r))
input)))
(process-input input [0 1 2 3 4])
output:
(#{{:b 0, :a 0} {:a 1, :b 100} {:a 2, :b 300}
{:b 0, :a 3} {:a 4, :b 0}}
#{{:a 0, :b 10} {:b 0, :a 1} {:b 0, :a 2}
{:b 0, :a 3} {:a 4, :b 50}})
My attempt:
(defn fill-in-missing [lists]
(let [[min max] (apply (juxt min max) (map :a (flatten lists)))]
(for [cur-list lists]
(for [i (range min (inc max))]
(merge {:a i :b 0}
(some #(when (= i (:a %)) %) cur-list))))))
To get the minimum and maximum values of :a I just collect every :a with map and flatten, then I use juxt so I can apply both the min and max functions to them at the same time.
Since we want two levels of nesting lists, I went for two for list comprehensions and tried to make an expression that would attempt to find the map in the input, or else return the default {:a i :b 0}.
What would be a quick way to keep only certain keys from a hash-map?
(def m {:a 1 :b 2 :c 3 :d 4})
explicit version:
((fn [{:keys [b c]}] {:b b :c c})
m)
;= {:b 2, :c 3}
select-keys:
(select-keys m [:b :c])
I have a lazy sequence of maps :
{:a 1 :b "a"} {:a 1 :b "b"} {:a 2 :b "a"} {:a 3 :b "a"} {:a 3 :b "b"} ...
and I want to group it by key :a and return another lazy seq:
[{:a 1 :b "a"} {:a 1 :b "b"}] [{:a 2 :b "a"}] [{:a 3 :b "a"} {:a 3 :b "b"}] ...
What would be the best way to achieve this?
user=> (def a [{:a 1 :b "a"} {:a 1 :b "b"} {:a 2 :b "a"} {:a 3 :b "a"} {:a 3 :b "b"}])
#'user/a
user=> (group-by :a a)
{1 [{:a 1, :b "a"} {:a 1, :b "b"}], 2 [{:a 2, :b "a"}], 3 [{:a 3, :b "a"} {:a 3, :b "b"}]}
user=> (map second (group-by :a a))
([{:a 1, :b "a"} {:a 1, :b "b"}] [{:a 2, :b "a"}] [{:a 3, :b "a"} {:a 3, :b "b"}])
If you have a map or a collection of maps and you'd like to be able to update the values of several keys with one function, what the the most idiomatic way of doing this?
=> (def m [{:a 2 :b 3} {:a 2 :b 5}])
#'user/m
=> (map #(update-in % [:a] inc) m)
({:a 3, :b 3} {:a 3, :b 5})
Rather than mapping update-in for each key, I'd ideally like some function that operates like this:
=> (map #(update-vals % [:a :b] inc) m)
({:a 3, :b 4} {:a 3, :b 6})
Any advice would be much appreciated! I'm trying to reduce the number of lines in an unnecessarily long script.
Whenever you need to iteratively apply a fn to some data, reduce is your friend:
(defn update-vals [map vals f]
(reduce #(update-in % [%2] f) map vals))
Here it is in action:
user> (def m1 {:a 2 :b 3})
#'user/m1
user> (update-vals m1 [:a :b] inc)
{:a 3, :b 4}
user> (def m [{:a 2 :b 3} {:a 2 :b 5}])
#'user/m
user> (map #(update-vals % [:a :b] inc) m)
({:a 3, :b 4} {:a 3, :b 6})