Search code examples
clojure

Finding max value using reduce


New to clojure with a java background. I have the following table and need to transform the table to a hash-map that maps products to the city that has the highest sale. For example, the output should look like:

{"Pencil": "Toronto"
"Bread": "Ottawa"}

(def table [
    {:product "Pencil"
    :city "Toronto"
    :year "2010"
    :sales "2653.00"}
    {:product "Pencil"
    :city "Oshawa"
    :year "2010"
    :sales "525.00"}
    {:product "Bread"
    :city "Toronto"
    :year "2010"
    :sales "136,264.00"}
    {:product "Bread"
    :city "Oshawa"
    :year "nil"
    :sales "242,634.00"}
    {:product "Bread"
    :city "Ottawa"
    :year "2011"
    :sales "426,164.00"}])

This is what I have so far:

(reduce (fn [product-cities {:keys [product sales]}]
         (update-in product-cities [product] (fnil conj []) sales))
       {}
       table)

This produces the outcome:

{"Bread"
["136,264.00"
"242,634.00"
"426,164.00"],
 "Pencil" ["2653.00" "525.00"]}

How can i compare the sales of each city and and only keep the name of the city with the highest sales? Having a really tough time with this. Thanks


Solution

  • there is a handy function max-key in clojure.core, that is perfectly suitable for this case:

    (defn process [table]
      (let [parseDouble #(Double/parseDouble (clojure.string/replace % #"," ""))]
        (->> table
             (group-by :product)
             (map (comp (juxt :product :city)
                        (partial apply max-key (comp parseDouble :sales))
                        val))
             (into {}))))
    
    user> (process table)
    ;;=> {"Pencil" "Toronto", "Bread" "Ottawa"}
    

    the key is that (partial apply max-key (comp parseDouble :sales)) part looks for the record in a group, having maximum parsed sales value.