Знаходжанне максімальнага значэння з дапамогай змяншэння

Новае ў Clojure з Java фону. У мяне ёсць наступныя табліцы і трэба пераўтварыць табліцу ў хэшы-карту, якая адлюстроўвае прадукты ў горад, які мае самыя высокія продажу. Напрыклад, выснова павінен выглядаць наступным чынам:

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

Як я магу параўнаць продажу кожнага горада і і толькі захаваць назва горада з самымі высокімі продажамі? Маючы вельмі цяжкі час з гэтым. дзякуй

2

6 адказы

ёсць зручная функцыя макс-ключ у clojure.core, што ідэальна падыходзіць для гэтага выпадку:

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

ключ, што (часткова прымяняюцца максімальны ключ (комп parseDouble: продажы)) частка выглядае для запісу ў групе, якія маюць максімальнае значэнне разабрана продажамі.

8
дададзена
Я напісаў бы амаль такі ж адказ - змяненне ў тым, што цяпер я хацеў бы выкарыстаць у з дапамогай датчыка, так як гэта дазваляе пазбегнуць, па меншай меры адзін узровень ляноты накладных выдаткаў. Наступным чынам: (у {} (карта (соед ,)) (гурт-па: табліцу прадукту)) .
дададзена аўтар glts, крыніца

Вам патрэбна функцыя, якая пераўтворыць значэнне продажаў з радкоў у лік. На дадзены момант у здагадцы, што лік продажаў сапраўды лік, гэта павінна зрабіць трук:

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

Replace identity with your string->number function.

Без сумневу, гэтая функцыя можа быць палепшана ...

3
дададзена

Вось як я б гэта зрабіць. Я spyx непрыгожа з бібліятэкі Тупело , каб візуалізаваць прамежкавыя этапы прасцей ( API Docs можна знайсці тут ). код:

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

Звярніце ўвагу, што першы крок павінен пераўтварыць усе значэння радкоў продажаў з якая плавае кропкай. Тады гэта будзе прасцей, калі мы выкарыстоўваем убудаваную функцыю групы, па , каб аддзяліць алоўкі ад хлеба, і г.д. Я хацеў, каб кожны крок асобна для прастаты мыслення, а таксама, так што я магу паставіць адладжваць раздруковак на кожным кроку.

ІМХО гэта прасцей, чым з дапамогай REPL, так як я магу заставацца ў маім любімым IDE/рэдактар ​​і матэрыял I тыпу захоўваецца ў файле, а не знікае, як толькі я патрапіў.

1
дададзена
Я думаю, што калі вы збіраецеся выкарыстоўваць неасноўныя бібліятэкі, то вы павінны зрабіць гэта відавочна ў кодзе. <�Код> forv функцыя Тупело дакладна? Чаму не т/forv ? Адзінае выключэнне, якое я магу думаць пра core.async , які мае некаторыя вельмі смешныя імёны функцый, якія вылучаюцца.
дададзена аўтар Chris Murphy, крыніца
Мой аргумент быў аб чытальнасці для іншых, а не зручнасць для сябе.
дададзена аўтар Chris Murphy, крыніца
Радок (т/см-Tupelo) ставіцца шэраг Вары у бягучае прастора імёнаў (замест : выкарыстоўваць п). Я аддаю перавагу гэты метад, як я выкарыстоўваю іх увесь час і стаміцца ​​ад дадатковых памылак друку рэ spyx , spyx непрыгожа , forv , <�код > dotest гэта = , xfirst , xsecond і г.д. Калі я не ўключыў увесь крыніца з НС і (т/см-Тупело) , то я згодны быць відавочна аб прасторы імёнаў будзе мець больш важнае значэнне.
дададзена аўтар Alan Thompson, крыніца

Вы можаце выкарыстоўваць нешта накшталт:

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

пастскрыптум Хоць папярэдні адказ можа быць больш зручным для чытання ...

1
дададзена

Вось даволі хутка версія, што дазваляе пазбегнуць прамежкавых структур дадзеных:

(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
дададзена

Ідэя дадатковага даходу на аснове @leetwinski адказу. Ідэя заключаецца ў выкарыстанні сартавання па па значэннях продажаў як гэта крыху больш асноўнага мовы.

(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
дададзена