2017-11-14 8 views
2

Javaのバックグラウンドで新しく追加する。私は次の表を持っており、テーブルを、最も売り上げが高い都市に製品をマップするハッシュマップに変換する必要があります。たとえば、出力は次のようになります。reduceを使用した最大値の検索

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

どのように私はの売り上げを比較することができますそれぞれの都市と最高の売上高を持つ都市の名前を保持するだけですか?これで本当に厳しい時を過す。ありがとう

答えて

8

、それはこの場合のために完全に適している:

(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))部分が最大解析された売上値を持つ、グループ内のレコードを探していることです。 @leetwinski回答に基づいて

(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)) 
+0

私はほぼ同じ答えを書いていたでしょう。これは少なくとも1つのレベルの怠惰のオーバーヘッドを避けるため、トランスデューサで 'into 'を使いたいというバリエーションです。このように: '(into {}(map(comp ,,,))(group-by:productテーブル))'。 – glts

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

があなたのstring->number機能をidentityを置き換え:今販売数が実際の数であると仮定するために、これはトリックを行う必要があります。

この機能を向上させることができる間違いありません...

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.前の答えはもっと読みやすいかもしれませんが...

-1

ここで私はそれをやります。私はspyx-prettyfrom 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などから分離するのが最も簡単です。私は、それぞれのステップを分かりやすくするために別々のステップを分けておくのが好きです。また、各ステップでデバッグ印刷を行うこともできます。

これは、私がお気に入りのIDE /エディタにとどまることができるので、REPLを使うよりも簡単です。入力したものは、ヒットするとすぐに消えるのではなく、ファイルに保存されます。 clojure.coreで便利な機能max-keyがある

+2

非コアライブラリを使用する場合は、コード内で明示的にする必要があります。 'forv'はtupelo関数ですよね?どうして 't/forv'?私が考えることができる唯一の例外は 'core.async'です。これは非常に面白い関数名があります。 –

+0

行 '(t/refer-tupelo)'は現在の名前空間への 'vars'の数を参照します(':use'節の代わりに)。私はいつもそれらを使い、 'spyx'、' spyx-pretty'、 'forv'、' dotest'' is = '、' xfirst'、 'xsecond'などの余分な型付けに疲れてこのメソッドを好む私が 'ns'と'(t/refer-tupelo) 'でソース全体を含めなかった場合は、名前空間がより重要であることを明示的に示すことに同意します。 –

+0

私の主張は、自分の利便性ではなく、他人の読みやすさに関するものでした。 –

0

はここで、中間データ構造を回避かなり速いバージョンです。 アイデアは、売上高の値にsort-byを使用することです。これは言語の基本的なものです。

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

スピンオフのアイデア: