I am merging two hashmaps in clojure, but it is giving unexpected results. Below are the two datasets I am merging:
({:TEST"E", :EMEA "0", :NA "0", :ASPAC "180"}
{:TEST"B", :EMEA "0", :NA "70", :ASPAC "0"}
{:TEST"D", :EMEA "38", :NA "0", :ASPAC "0"}
{:TEST"C", :EMEA "0", :NA "0", :ASPAC "0"}
{:TEST"G", :EMEA "360", :NA "0", :ASPAC "0"}
{:TEST"A", :EMEA "45", :NA "0", :ASPAC "0"}
{:TEST"F", :EMEA "0", :NA "0", :ASPAC "66"})
({:TEST"A", :EMEA_1 "40", :NA_1 "0", :ASPAC_1 "0"}
{:TEST"B", :EMEA_1 "90", :NA_1 "0", :ASPAC_1 "0"}
{:TEST"H", :EMEA_1 "0", :NA_1 "120", :ASPAC_1 "0"}
{:TEST"C", :EMEA_1 "0", :NA_1 "85", :ASPAC_1 "0"})
I am expecting to see somethig like this:
({:TEST"A", :EMEA "45", :NA "0", :ASPAC "0", :EMEA_1 "40", :NA_1 "0", :ASPAC_1 "0"}
{:TEST"B", :EMEA "0", :NA "70", :ASPAC "0", :EMEA_1 "90", :NA_1 "0", :ASPAC_1 "0"}
{:TEST"C", :EMEA "0", :NA "0", :ASPAC "0", :EMEA_1 "0", :NA_1 "85", :ASPAC_1 "0"}
{:TEST"D", :EMEA "38", :NA "0", :ASPAC "0", :EMEA_1 nil, :NA_1 nil, :ASPAC_1 nil}
{:TEST"E", :EMEA "0", :NA "0", :ASPAC "180", :EMEA_1 nil, :NA_1 nil, :ASPAC_1 nil}
{:TEST"F", :EMEA "0", :NA "0", :ASPAC "66", :EMEA_1 nil, :NA_1 nil, :ASPAC_1 nil}
{:TEST"G", :EMEA "360", :NA "0", :ASPAC "0", :EMEA_1 nil, :NA_1 nil, :ASPAC_1 nil}
{:TEST"H", :EMEA nil, :NA nil, :ASPAC nil, :EMEA_1 "0", :NA_1 "120", :ASPAC_1 "0"})
I have tried using merge, merge-wth, apply merge-with but nothing is giving me the results I expected.
Any adivse on how I can get to my desired resultset or why I am actually not geting what I am expecting would also be helpful.
Thanks you.
Given your two collections are defined as coll1 and coll2:
(map (partial apply merge) (-> (clojure.set/union coll1 coll2)
(clojure.set/index [:TEST])
vals))
should lead to your desired result.
If you don't want clojure.set you may also use
(map (partial apply merge) (->> (concat coll1 coll2)
(group-by :TEST)
vals))
However looking at your data structures, they rather look like a case for clojure.set.
You may note that the result will not have [:key nil] like entries. If you want them, there certainly is a way but relying on them goes against the meaning of nil as nothing.
Related
How to make the following code generate a similar visualization to pprint in HTML?
(let [clj-structure {:ui {:selected #{[:A 3]}}
:domain
{:C {0 {:value 12}}
:D {0 {:dependants [[:C 0]] :string "3" :value "3"}
1 {:dependants [[:C 0]] :string "4" :value "4"}}
:E {2 {:dependants [] :string "2" :value "2"}
3 {:dependants [] :string "10" :value "10"}}}}]
[:p (str clj-structure)])
(let [clj-structure {:ui {:selected #{[:A 3]}}
:domain
{:C {0 {:value 12}}
:D {0 {:dependants [[:C 0]] :string "3" :value "3"}
1 {:dependants [[:C 0]] :string "4" :value "4"}}
:E {2 {:dependants [] :string "2" :value "2"}
3 {:dependants [] :string "10" :value "10"}}}}]
[:pre (with-out-str (cljs.pprint/pprint clj-structure))])
Let's say I have a list of lists representing a tree-structure in clojure, like
'(a (b (c d)) (e (f)))
and I want to translate it into a record format like this (for the purpose of passing it off to a visualization package):
[{:id "0" :label "a" :parent nil}
{:id "1" :label "b" :parent "0"}
{:id "2" :label "c" :parent "1"}
{:id "3" :label "d" :parent "1"}
{:id "4" :label "e" :parent "0"}
{:id "5" :label "f" :parent "4"}]
What's the right way to go about this?
I am pretty shaky with this, but I would think of starting with defrecord, and then some way of looping through the tree, but I don't know how to get started.
(def tree '(a (b (c d)) (e (f))))
(defn list-to-record [l]
(defrecord rec [id name parent])
(let [counter (atom 0)]
(into [] (map ->rec
... ... ...))))
(list-to-record tree)
Perhaps I should be using clojure.walk?
Edit: to clarify, this should work regardless of what the labels are, so changing the labels in the input list shouldn't do anything to the resulting structure (the :parent values for each :id). That is, the following list, just as above, but with the labels all the same as each other
'(a (a (a a)) (a (a)))
should get translated into
[{:id "0" :label "a" :parent nil}
{:id "1" :label "a" :parent "0"}
{:id "2" :label "a" :parent "1"}
{:id "3" :label "a" :parent "1"}
{:id "4" :label "a" :parent "0"}
{:id "5" :label "a" :parent "4"}]
Here's a way to do it with Clojure zippers and loop + recur:
(defn index-zipper [z]
(loop [loc z, next-id 0, parent-ids [], acc []]
(cond
(z/end? loc) acc
(and (z/node loc) (not (z/branch? loc)))
(recur
(z/next loc)
(inc next-id)
(cond
(some-> (z/right loc) z/branch?) (conj parent-ids next-id)
(not (z/right loc)) (some-> parent-ids not-empty pop)
:else parent-ids)
(conj acc
{:id (str next-id)
:label (str (z/node loc))
:parent (when (seq parent-ids)
(str (peek parent-ids)))}))
:else
(recur (z/next loc) next-id parent-ids acc))))
The loop has bindings for:
Current zipper location
Next :id value, to be incremented each time we see a leaf node
Stack (vector) of current parent :ids, which will be pushed/popped as the zipper descends/ascends. The :parent value for each leaf node is on the top of the parent-ids stack.
accumulator vector for leaf node maps
You can call the function with a zipper:
(index-zipper (z/seq-zip '(a (b (c d)) (e (f)))))
=>
[{:id "0", :label "a", :parent nil}
{:id "1", :label "b", :parent "0"}
{:id "2", :label "c", :parent "1"}
{:id "3", :label "d", :parent "1"}
{:id "4", :label "e", :parent "0"}
{:id "5", :label "f", :parent "4"}]
(index-zipper (z/seq-zip '(a (a (a a)) (a (a)))))
=>
[{:id "0", :label "a", :parent nil}
{:id "1", :label "a", :parent "0"}
{:id "2", :label "a", :parent "1"}
{:id "3", :label "a", :parent "1"}
{:id "4", :label "a", :parent "0"}
{:id "5", :label "a", :parent "4"}]
Here is one way to do it. You just have to add the part about assigning an "id" to each node.
Note also that you should re-format your input data so each node is in Hiccup format (i.e. even singleton nodes w/o children are wrapped in a vector).
(ns tst.demo.core
(:use demo.core tupelo.core tupelo.test)
(:require [tupelo.core :as t]))
(def tree
[:a
[:b
[:c]
[:d]]
[:e
[:f]]])
(def relationships (atom []))
(defn save-relationships
[parent-id curr-node]
(let [curr-id (first curr-node)
kid-nodes (rest curr-node)]
(swap! relationships #(conj % {:parent parent-id :label curr-id}))
(doseq [kid-node kid-nodes]
(save-relationships curr-id kid-node))))
(dotest
(reset! relationships [])
(save-relationships nil tree)
(spyx-pretty #relationships))
with result:
~/expr/demo >
~/expr/demo > lein test
lein test _bootstrap
-------------------------------
Clojure 1.10.1 Java 12
-------------------------------
lein test tst.demo.core
(clojure.core/deref relationships) =>
[{:parent nil, :label :a}
{:parent :a, :label :b}
{:parent :b, :label :c}
{:parent :b, :label :d}
{:parent :a, :label :e}
{:parent :e, :label :f}]
Ran 2 tests containing 0 assertions.
0 failures, 0 errors.
I have a set of maps as below
#{{:uid 2, :name "book2", :qty "2", :price "2"} {:uid 3, :name "book3", :price "1", :qty "1"}
{:uid 1, :name "book4", :qty "2", :price "2"}}
Now I want to read value of :uid with greatest number, so in this example I want to get 3.
Can anyone guide me how can I fetch this?
You could also use max-key
(:uid (apply max-key
:uid
#{{:uid 2, :name "book2", :qty "2", :price "2"} {:uid 3, :name "book3", :price "1", :qty "1"}
{:uid 1, :name "book4", :qty "2", :price "2"}}))
This wouldn't work if the data was as given though, as the uids seem to be strings in some and numbers in other maps. Also, if the set was empty, this would fail.
The built in sorted-set-by function should do the trick:
user> (apply sorted-set-by
#(- (:uid %1) (:uid %2))
#{{:uid 2, :name "book2", :qty "2", :price "2"}
{:uid 3, :name "book3", :price "1", :qty "1"}
{:uid 1, :name "book4", :qty "2", :price "2"}})
#{{:uid 1, :name "book4", :qty "2", :price "2"}
{:uid 2, :name "book2", :qty "2", :price "2"}
{:uid 3, :name "book3", :qty "1", :price "1"}}
then take the last one (or reverse the %1 and %2 comparator). Of course the usual trade-offs apply if for instance you where only going to get the value once and or it was a small set then a linear scan would be more efficient.
PS: I changed the data so all the uids are the same type
You could try using reduce
(reduce #(max %1 (:uid %2)) 0 #{{:uid 2, :name "book2", :qty "2", :price "2"}
{:uid 3, :name "book3", :price "1", :qty "1"}})
Assuming the uids are non-negative.
EDIT:
It would be better to use a different starting value for the accumulator as recommended by #Thumbnail.
(reduce #(max %1 (:uid %2)) (Long/MIN_VALUE) #{{:uid 2, :name "book2", :qty "2", :price "2"}
{:uid 3, :name "book3", :price "1", :qty "1"}})
This should work with negatives as well.
If you just want the number,
(defn max-uid [coll] (apply max (map :uid coll)))
will supply it.
(max-uid #{{:uid 2, :name "book2", :qty "2", :price "2"}
{:uid 3, :name "book3", :price "1", :qty "1"}
{:uid 1, :name "book4", :qty "2", :price "2"}})
;3
I have a hash map in clojure which contains some nil values. I am trying to group the data and sum the values, this gives me a null pointer due to the nil values. Can someone please advise on how I can iterate through the hash map and replace all nil values with integer 0?
(def data [{:MEDAL "SILVER" :EMEA 1 :NA nil :ASPAC 3}
{:MEDAL "GOLD" :EMEA 1 :NA 2 :ASPAC 3}
{:MEDAL "GOLD" :EMEA nil :NA 2 :ASPAC nil}
{:MEDAL "BRONZE" :EMEA nil :NA 2 :ASPAC 3}
{:MEDAL "SILVER" :EMEA 1 :NA 2 :ASPAC 3}
{:MEDAL "GOLD" :EMEA 1 :NA nil :ASPAC nil}
{:MEDAL "BRONZE" :EMEA 1 :NA 2 :ASPAC 3}])
Thanks
(map (fn [m]
(into {}
(map (fn [[k v]]
[k (if (nil? v) 0 v)]) m)))
data)
=> ({:EMEA 1, :NA 0, :MEDAL "SILVER", :ASPAC 3}
{:EMEA 1, :NA 2, :MEDAL "GOLD", :ASPAC 3}
{:EMEA 0, :NA 2, :MEDAL "GOLD", :ASPAC 0}
{:EMEA 0, :NA 2, :MEDAL "BRONZE", :ASPAC 3}
{:EMEA 1, :NA 2, :MEDAL "SILVER", :ASPAC 3}
{:EMEA 1, :NA 0, :MEDAL "GOLD", :ASPAC 0}
{:EMEA 1, :NA 2, :MEDAL "BRONZE", :ASPAC 3})
Rather than replace the nil values with zeros, you might consider just working with them by using keep. For example:
(apply + (keep :NA data))
; 10
(apply + (keep (fn [m] (when (= (:MEDAL m) "SILVER") (:EMEA m))) data))
; 2
You could use fnil.
(->> data
(apply merge-with conj (zipmap [:EMEA :NA :MEDAL :ASPAC] (repeat [])))
(reduce-kv #(assoc %1 %2 (reduce (fnil + 0 0) 0 %3)) {}))
Obviously :MEDAL needs other treatment.
I am assuming that you want to group the data by :MEDAL and then add EMEA, ASPAC etc., right?
;; Helper function
(defn batch-hashmaps
"Applies operation on values of keys in maps. filterf filters valid values.
Example: (batch-hashmaps + [:b :c] number? {:a 30, :b 50, :c :cheese} {:b 30, :c 40})
=> {:b 80, :c 40}"
[operation keys filterf & maps]
(apply conj {}
(for [key keys]
[key (apply operation (filter filterf (map #(key %) maps)))])))
(for [medal (set (map #(:MEDAL %) data))]
(assoc (apply (partial batch-hashmaps + [:EMEA :NA :ASPAC] number?) (filter #(= medal (:MEDAL %)) data) ) :MEDAL medal))
This should be the desired result:
({:MEDAL "GOLD", :EMEA 2, :NA 4, :ASPAC 3}
{:MEDAL "SILVER", :EMEA 2, :NA 2, :ASPAC 6}
{:MEDAL "BRONZE", :EMEA 1, :NA 4, :ASPAC 6})
And here is another quick and dirty thing.
(defn sum-medal
[medal data]
(assoc (apply (partial merge-with (fn [& [_ _ :as vals]] (apply + (filter number? vals)))) (filter #(= (:MEDAL %) medal) data)) :MEDAL medal))
I am trying to sort the below map( Clojure) on the basis of "col_nm" field, but unable to do so.
{:Mawb {:user_val "3", :col_nm "1"},
:HawbDate {:user_val "", :col_nm "3"},
:EtlBatchID {:user_val "1", :col_nm "2"}}
The output should be:
{:Mawb {:user_val "3", :col_nm "1"},
:EtlBatchID {:user_val "1", :col_nm "2"},
:HawbDate {:user_val "", :col_nm "3"} }
Can anyone help me, thanks in advance.
Try this one:
(def m {:Mawb {:user_val "3", :col_nm "1"},
:HawbDate {:user_val "", :col_nm "3"},
:EtlBatchID {:user_val "1", :col_nm "2"}})
(sort-by (comp :col_nm second) m)
=> ([:Mawb {:user_val "3", :col_nm "1"}]
[:EtlBatchID {:user_val "1", :col_nm "2"}]
[:HawbDate {:user_val "", :col_nm "3"}])