2017-11-14 12 views
2

자바 배경이있는 새로운 항목. 나는 다음 테이블을 가지고 있으며 가장 높은 판매량을 가진 도시로 제품을 매핑하는 해시 맵으로 테이블을 변환해야합니다.reduce를 사용하여 최대 값 찾기

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

가 어떻게의 판매를 비교할 수 있습니다 :이 결과를 생산

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

:

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

이것은 내가 지금까지 무엇을 가지고 예를 들어, 출력이 같아야합니다 각 도시와 가장 높은 판매량을 가진 도시 이름 만 지키면됩니까? 이것으로 정말 힘든 시간을 보내십시오. 감사합니다

답변

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)) 부분은 그룹의 레코드를 가진 최대 구문 분석 판매량을 보이는 것입니다. @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

필자는 거의 동일한 대답을 작성했을 것입니다. 변형은 적어도 하나의 게으름 수준의 오버 헤드를 피하기 때문에 트랜스 듀서로 '입력'을 사용하는 것과 같습니다. 다음과 같이 :'({group} : product 테이블에) {} (map (comp ,,,)))'. – 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))) 

결과는 다음과 같습니다 1 단계는 부동 소수점 모든 문자열 판매 값을 변환하는 것입니다

--------------------------------------- 
    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을 사용하여 빵 등을 연필에서 분리하는 것이 가장 쉽습니다. 생각을 쉽게하기 위해 각 단계를 구분하고 각 단계마다 디버그 출력물을 넣을 수 있습니다.

IMHO 내가 좋아하는 IDE/편집기에 머물러있어 REPL을 사용하는 것보다 더 간단합니다. 입력 한 내용은 내가 마자 마자 사라지 자마자 파일에 저장됩니다.

+2

비 핵심 라이브러리를 사용하려는 경우 코드에서 명시 적으로 지정해야한다고 생각합니다. 'forv'는 tupelo 함수입니다. 왜't/forv'가 아닌가? 내가 생각할 수있는 유일한 예외는'core.async'입니다.이 함수는 매우 재미있는 함수 이름을 가지고 있습니다. –

+0

'(t/refer-tupelo)'줄은 (: use 절 대신) 현재의 네임 스페이스에있는'vars '를 참조합니다. 필자는 항상이 방법을 선호하며'spyx','spyx-pretty','forv','dotest'는 =,'xfirst','xsecond' 등의 여분의 타이핑에 질려 버립니다. . 만약 전체 소스를'ns'와'(t/refer-tupelo) '에 포함시키지 않았다면, 네임 스페이스가 더 중요하다는 것에 대해 명시 적으로 동의하는 것에 동의합니다. –

+0

내 주장은 자신을위한 편의성보다는 다른 사람들을위한 가독성에 관한 것이 었습니다. –

0

여기에 중간 데이터 구조를 피하는 꽤 빨리 버전입니다. 아이디어는 언어에서 조금 더 기본이기 때문에 판매 값에 정렬 기준을 사용하는 것입니다.

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