使用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"]}
我如何比较每个城市的销售情况,并且只保留销售额最高的城市名称?对此非常艰难。感谢
答案 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 /编辑器中,我输入的内容会保存在文件中,而不是在我点击后立即消失。