使用reduce查找最大值

时间:2017-11-14 17:31:30

标签: clojure

使用java背景的clojure新手。我有下表,需要将表转换为哈希映射,将产品映射到销售额最高的城市。例如,输出应如下所示:

{"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"}])

这是我到目前为止所做的:

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

这会产生结果:

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

我如何比较每个城市的销售情况,并且只保留销售额最高的城市名称?对此非常艰难。感谢

6 个答案:

答案 0 :(得分:8)

clojure.core中有一个方便的函数max-key,非常适合这种情况:

(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"}

关键是(partial apply max-key (comp parseDouble :sales))部分查找组中的记录,具有最大解析销售价值。

答案 1 :(得分:3)

您需要一些将销售值从字符串转换为数字的功能。现在假设销售数字确实是数字,这应该可以解决问题:

(->> table
     (group-by :product)
     (map (fn [[k v]]
            [k (first (sort-by (comp - identity :sales) v))]))
     (into {})
     vals
     (map (comp #(apply vector %)
                vals
                #(select-keys % [:product :city])))
     (into {}))

identity替换为您的string->number功能。

毫无疑问,这个功能可以改进......

答案 2 :(得分:1)

您可以使用以下内容:

(into {} (map (fn [[k {:keys [city sales]}]] [k city])
                (reduce (fn [product-cities {:keys [product sales city]}]
                          (let [sales (Double/parseDouble (clojure.string/replace sales "," ""))
                                prev-sales (get-in product-cities [product :sales] 0)]
                            (if (> sales prev-sales)
                              (assoc product-cities product {:sales sales :city city})
                              product-cities)))
                        {}
                        table)))

P.S。虽然以前的答案可能更具可读性......

答案 3 :(得分:0)

这是一个非常快的版本,可以避免中间数据结构:

(let [parse #(Double/parseDouble (clojure.string/replace % "," ""))]
  (reduce (fn [m {:keys [product sales city] :as cand}]
            (let [sales-d (parse sales)]
              (update m product (fn [prev]
                                  (if (or (nil? prev) (< (:sales prev) sales-d))
                                    (assoc cand :sales sales-d) 
                                    prev)))))
          {} products))

答案 4 :(得分:0)

基于@leetwinski答案的衍生创意。 想法是在销售中使用排序&#39;这些价值因为它在语言中更为基础。

(defn process [table]
  (let [parseDouble #(Double/parseDouble (clojure.string/replace % #"," ""))
        parsedTable (for [a table] (update a :sales parseDouble))]
    (->> parsedTable
         (sort-by :sales)
         (group-by :product)
         vals        
         (map (comp (juxt :product :city) last))
         (into {}))))
 (process table)
=>{"Bread" "Ottawa" "Pencil" "Toronto"}

答案 5 :(得分:-1)

我将如何做到这一点。我使用spyx-pretty from the Tupelo library来简化中间步骤的可视化(API docs can be found here)。代码:

(ns tst.demo.core
  (:use demo.core
        tupelo.test)
  (:require [tupelo.core :as t]
            [clojure.string :as str] ))
(t/refer-tupelo)

(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"}])

(defn str->double
  "Convert a string like '2,123.97' to a double like 2123.97 "
  [str-val]
  (let [no-commas (str/replace str-val #"," "")
        dbl-val   (Double/parseDouble no-commas)]
    dbl-val))

(dotest
  (let [table-num (forv [item table]
                    (update item :sales str->double))
        grouped   (group-by :product table-num)
        >>        (spyx-pretty grouped)
        group-max (forv [group grouped]
                    (do
                      (spyx-pretty group)
                      (let [records        (xsecond group)
                            >>             (spyx-pretty records)
                            records-sorted (sort-by :sales > records)
                            >>             (spyx-pretty records-sorted)
                            max-rec        (xfirst records-sorted)
                            ]
                        (spyx max-rec))))]
    (spyx-pretty group-max)))

结果是:

---------------------------------------
   Clojure 1.9.0-beta1    Java 9.0.1
---------------------------------------

Testing tst.demo.core

grouped => 
{"Pencil"
 [{:product "Pencil", :city "Toronto", :year "2010", :sales 2653.0}
  {:product "Pencil", :city "Oshawa", :year "2010", :sales 525.0}],
 "Bread"
 [{:product "Bread", :city "Toronto", :year "2010", :sales 136264.0}
  {:product "Bread", :city "Oshawa", :year "nil", :sales 242634.0}
  {:product "Bread", :city "Ottawa", :year "2011", :sales 426164.0}]}

group => 
["Pencil"
 [{:product "Pencil", :city "Toronto", :year "2010", :sales 2653.0}
  {:product "Pencil", :city "Oshawa", :year "2010", :sales 525.0}]]

records => 
[{:product "Pencil", :city "Toronto", :year "2010", :sales 2653.0}
 {:product "Pencil", :city "Oshawa", :year "2010", :sales 525.0}]

records-sorted => 
({:product "Pencil", :city "Toronto", :year "2010", :sales 2653.0}
 {:product "Pencil", :city "Oshawa", :year "2010", :sales 525.0})
max-rec => {:product "Pencil", :city "Toronto", :year "2010", :sales 2653.0}

group => 
["Bread"
 [{:product "Bread", :city "Toronto", :year "2010", :sales 136264.0}
  {:product "Bread", :city "Oshawa", :year "nil", :sales 242634.0}
  {:product "Bread", :city "Ottawa", :year "2011", :sales 426164.0}]]

records => 
[{:product "Bread", :city "Toronto", :year "2010", :sales 136264.0}
 {:product "Bread", :city "Oshawa", :year "nil", :sales 242634.0}
 {:product "Bread", :city "Ottawa", :year "2011", :sales 426164.0}]

records-sorted => 
({:product "Bread", :city "Ottawa", :year "2011", :sales 426164.0}
 {:product "Bread", :city "Oshawa", :year "nil", :sales 242634.0}
 {:product "Bread", :city "Toronto", :year "2010", :sales 136264.0})
max-rec => {:product "Bread", :city "Ottawa", :year "2011", :sales 426164.0}

group-max => 
[{:product "Pencil", :city "Toronto", :year "2010", :sales 2653.0}
 {:product "Bread", :city "Ottawa", :year "2011", :sales 426164.0}]

请注意,第一步是将所有字符串销售值转换为浮点数。然后,最简单的方法是使用内置函数group-by将铅笔与Bread等分开。我喜欢将每一步分开以便于思考,同时我也可以在每一步中调试打印输出

恕我直言,这比使用REPL更简单,因为我可以留在我最喜欢的IDE /编辑器中,我输入的内容会保存在文件中,而不是在我点击后立即消失。

相关问题