Reputation: 357
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
Upvotes: 2
Views: 2684
Reputation: 9266
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))
Upvotes: 0
Reputation: 9266
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})
Upvotes: 0
Reputation: 17299
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.
Upvotes: 0
Reputation: 3584
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
Upvotes: 1
Reputation: 17773
(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})
Upvotes: 1