redhands
redhands

Reputation: 357

replace nil values with zero in hash map

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

Answers (5)

Leon Grapenthin
Leon Grapenthin

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

Leon Grapenthin
Leon Grapenthin

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

kotarak
kotarak

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

ToBeReplaced
ToBeReplaced

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

Joost Diepenmaat
Joost Diepenmaat

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

Related Questions