2016-03-23 9 views
0

Ich habe diese Werte in einem Hash:Ruby - Wie summiere ich Werte eines Hashs basierend auf einem bestimmten Datum (= Schlüssel)?

{nil=>0, 
Thu, 03 Dec 2015=>#<BigDecimal:7ff496381db8,'0.151875E2',18(27)>, 
Fri, 04 Dec 2015=>#<BigDecimal:7ff496381cf0,'0.214375E2',18(27)>, 
Wed, 09 Dec 2015=>#<BigDecimal:7ff496381c28,'0.6229E2',18(27)>, 
Thu, 10 Dec 2015=>#<BigDecimal:7ff496381b60,'0.1243E2',18(27)>, 
Fri, 11 Dec 2015=>#<BigDecimal:7ff496381a98,'0.1243E2',18(27)>, 
Mon, 14 Dec 2015=>#<BigDecimal:7ff4963819d0,'0.6611E2',18(27)>, 
Tue, 15 Dec 2015=>#<BigDecimal:7ff496381908,'0.625E1',18(18)>, 
Wed, 16 Dec 2015=>#<BigDecimal:7ff496381840,'0.73345E2',18(27)>, 
Thu, 17 Dec 2015=>#<BigDecimal:7ff496381778,'0.31845E2',18(27)>, 
Fri, 18 Dec 2015=>#<BigDecimal:7ff4963816b0,'0.409225E2',18(27)>, 
Mon, 21 Dec 2015=>#<BigDecimal:7ff4963815e8,'0.8019E2',18(27)>, 
Mon, 28 Dec 2015=>#<BigDecimal:7ff496381520,'0.3125E2',18(27)>, 
Mon, 04 Jan 2016=>#<BigDecimal:7ff496381458,'0.125E2',18(27)>, 
Wed, 06 Jan 2016=>#<BigDecimal:7ff496381390,'0.625E2',18(27)>, 
Thu, 07 Jan 2016=>#<BigDecimal:7ff4963812c8,'0.9111E2',18(27)>, 
Fri, 08 Jan 2016=>#<BigDecimal:7ff4963811d8,'0.11972E3',18(27)>, 
Mon, 11 Jan 2016=>#<BigDecimal:7ff4963810e8,'0.5022E2',18(27)>, 
Wed, 13 Jan 2016=>0, Thu, 14 Jan 2016=>0, Fri, 15 Jan 2016=>0, 
Wed, 09 Mar 2016=>#<BigDecimal:7ff496380eb8,'0.258125E2',18(27)>, 
Tue, 15 Mar 2016=>#<BigDecimal:7ff496380da0,'0.631825E2',18(27)>, 
Wed, 16 Mar 2016=>#<BigDecimal:7ff496380cd8,'0.504225E2',18(27)>, 
Thu, 17 Mar 2016=>#<BigDecimal:7ff496380c10,'0.125E2',18(27)>, 
Fri, 18 Mar 2016=>#<BigDecimal:7ff496380b48,'0.631825E2',18(27)>, 
Mon, 21 Mar 2016=>#<BigDecimal:7ff496380a80,'0.167925E2',18(27)>, 
Tue, 22 Mar 2016=>0} 

ich durch einige Kalenderdaten am Looping (@dates) und von hier ich zwei Variablen ein bestimmtes Jahr und Monat enthalten:

@dates.each do |d| 
    current_yer = d.strftime('%Y') #2016 
    current_month = d.strftime('%m') # 01 - january 

Jetzt, Ich habe Variablen, die die Daten enthalten, ich möchte eine Summe aller Daten im Hash ausgeben; so in diesem Fall würde Ich mag so etwas wie dies als Ausgabe erhalten:

2016 01: Summe aller Januar Elemente in der Hash-

Insbesondere Summe dieser:

Mon, 04 Jan 2016=>#<BigDecimal:7ff496381458,'0.125E2',18(27)>, 

Wed, 06 Jan 2016=>#<BigDecimal:7ff496381390,'0.625E2',18(27)>, 

Thu, 07 Jan 2016=>#<BigDecimal:7ff4963812c8,'0.9111E2',18(27)>, 

Fri, 08 Jan 2016=>#<BigDecimal:7ff4963811d8,'0.11972E3',18(27)>, 

Mon, 11 Jan 2016=>#<BigDecimal:7ff4963810e8,'0.5022E2',18(27)>, 

Wed, 13 Jan 2016=>0, Thu, 14 Jan 2016=>0, Fri, 15 Jan 2016=>0, 

Wie geht das?

Vielen Dank im Voraus.

Antwort

1

können Sie versuchen, mit select und sum:

@dates.select{|d, _| d.strftime('%Y %m') == '2016 01'}.values.sum 
+0

Beachten Sie, dass einer der '@ dates'‘ Schlüssel ist 'nil'. –

0

Construct Beispiel Hash

Lassen Sie uns zunächst einen Hash-Konstrukt (h), die ähnlich wie bei Ihnen ist, aber ein bisschen kleiner:

g = { 
    "Thu, 03 Dec 2015"=> 1, 
    "Fri, 11 Dec 2015"=> 2, 
    "Mon, 14 Dec 2015"=> 3, 
    "Tue, 15 Dec 2015"=> 4, 
    "Wed, 16 Dec 2015"=> 5, 
    "Fri, 18 Dec 2015"=> 6, 
    "Mon, 21 Dec 2015"=> 7, 
    "Mon, 04 Jan 2016"=> 8, 
    "Fri, 08 Jan 2016"=> 9, 
    "Wed, 13 Jan 2016"=> 0, 
    "Thu, 14 Jan 2016"=> 0, 
    "Wed, 09 Mar 2016"=>10, 
    "Tue, 15 Mar 2016"=>11, 
    "Wed, 16 Mar 2016"=>12, 
    "Mon, 21 Mar 2016"=>13, 
    "Tue, 22 Mar 2016"=> 0 } 

require 'date' 
require 'bigdecimal' 

h = { nil=>0 }.tap { |h| g.each { |k,v| 
    h[Date.strptime(k, "%a, %d %b %Y")] = v.zero? ? 0 : BigDecimal.new(v) } } 

#=> {nil=>0, 
    # #<Date: 2015-12-03 ((2457360j,0s,0n),+0s,2299161j)> => 
    #  #<BigDecimal:7faef915c8e0,'0.1E1',9(27)>, 
    # #<Date: 2015-12-11 ((2457368j,0s,0n),+0s,2299161j)> => 
    #  #<BigDecimal:7faef914ff28,'0.2E1',9(27)>, 
    # #<Date: 2015-12-14 ((2457371j,0s,0n),+0s,2299161j) >=> 
    #  #<BigDecimal:7faef914f938,'0.3E1',9(27)>, 
    # #<Date: 2015-12-15 ((2457372j,0s,0n),+0s,2299161j)> => 
    #  #<BigDecimal:7faef914f7a8,'0.4E1',9(27)>, 
    # #<Date: 2015-12-16 ((2457373j,0s,0n),+0s,2299161j)> => 
    #  #<BigDecimal:7faef914f320,'0.5E1',9(27)>, 
    # #<Date: 2015-12-18 ((2457375j,0s,0n),+0s,2299161j)> => 
    #  #<BigDecimal:7faef914e8d0,'0.6E1',9(27)>, 
    # #<Date: 2015-12-21 ((2457378j,0s,0n),+0s,2299161j)> => 
    #  #<BigDecimal:7faef914dde0,'0.7E1',9(27)>, 
    # #<Date: 2016-01-04 ((2457392j,0s,0n),+0s,2299161j)> => 
    #  #<BigDecimal:7faef914dca0,'0.8E1',9(27)>, 
    # #<Date: 2016-01-08 ((2457396j,0s,0n),+0s,2299161j)> => 
    #  #<BigDecimal:7faef914d390,'0.9E1',9(27)>, 
    # #<Date: 2016-01-13 ((2457401j,0s,0n),+0s,2299161j)> => 
    #  0, 
    # #<Date: 2016-01-14 ((2457402j,0s,0n),+0s,2299161j)> => 
    #  0, 
    # #<Date: 2016-03-09 ((2457457j,0s,0n),+0s,2299161j)> => 
    #  #<BigDecimal:7faef914cd28,'0.1E2',9(27)>, 
    # #<Date: 2016-03-15 ((2457463j,0s,0n),+0s,2299161j)> => 
    #  #<BigDecimal:7faef913ff60,'0.11E2',9(27)>, 
    # #<Date: 2016-03-16 ((2457464j,0s,0n),+0s,2299161j)> => 
    #  #<BigDecimal:7faef913f8d0,'0.12E2',9(27)>, 
    # #<Date: 2016-03-21 ((2457469j,0s,0n),+0s,2299161j)> => 
    #  #<BigDecimal:7faef913f560,'0.13E2',9(27)>, 
    # #<Date: 2016-03-22 ((2457470j,0s,0n),+0s,2299161j)> => 
    #  0 
} 

Hier habe ich die Klassenmethoden Date::strptime und BigDecimal::new verwendet.

Summe BigDecimal Werte für Monat

Wir haben jetzt Hash#reject, Enumerable#group_by, Enumerable#map, Enumerable#reduce (aka inject) und Array#to_h die Ergebnisse zu erhalten, verwenden können, benötigen Sie:

sums = 
h.reject { |k,_| k.nil? }. 
    group_by { |k,_| [k.year, k.month] }. 
    map { |yr_and_mon, arr| [yr_and_mon, arr.reduce(0) { |t,(_,bd)| t+bd }] }. 
    to_h 
    #=> {[2015, 12]=>#<BigDecimal:7faef9197eb8,'0.28E2',9(18)>, 
    # [2016, 1]=>#<BigDecimal:7faef9197b70,'0.17E2',9(18)>, 
    # [2016, 3]=>#<BigDecimal:7faef9197760,'0.46E2',9(18)>} 

Wir sehen, dass die BigDecimal Werte einfacher durch Umwandlung in Ganzzahlen:

sums.merge(sums) { |*,v| v.to_i } 
    #=> {[2015, 12]=>28, [2016, 1]=>17, [2016, 3]=>46} 

Vergleichen Sie diese Ergebnisse mit dem Hash g am Anfang dieser Antwort.

Ich verwendete die Form Hash#merge, die einen Block verwendet, um die Werte der Schlüssel zu bestimmen, die in beiden Hashes vorhanden sind, die zusammengeführt werden. Da ich sums mit sich selbst zusammenfasse, wird der Block verwendet, um die Werte aller Schlüssel zu bestimmen.

Sobald Sie den Hash sums haben, ist es eine einfache Aufgabe, die Summen nach Monat und Jahr in einem beliebigen Format zu drucken.

Die Schritte

h1 = h.reject { |k,_| k.nil? }.group_by { |k,_| [k.year, k.month] } 
    #=> {[2015, 12]=>[[#<Date: 2015-12-03 ((2457360j,0s,0n),+0s,2299161j)>, 
    #     #<BigDecimal:7faef915c8e0,'0.1E1',9(27)>], 
    #     [#<Date: 2015-12-11 ((2457368j,0s,0n),+0s,2299161j)>, 
    #     #<BigDecimal:7faef914ff28,'0.2E1',9(27)>], 
    ... 
    #     #<BigDecimal:7faef9093850,'0.46E2',9(18)>]] 
a1 = h1.map { |yr_and_mon, arr| [yr_and_mon, arr.reduce(0) { |t,(_,bd)| t+bd }] } 
    #=> [[[2015, 12], #<BigDecimal:7faef9029dd8,'0.28E2',9(18)>], 
    # [[2016, 1], #<BigDecimal:7faef90296a8,'0.17E2',9(18)>], 
    # [[2016, 3], #<BigDecimal:7faef9028dc0,'0.46E2',9(18)>]] 
a1.to_h 
    #=> {[2015, 12]=>#<BigDecimal:7faef9098710,'0.28E2',9(18)>, 
    # [2016, 1]=>#<BigDecimal:7faef9093da0,'0.17E2',9(18)>, 
    # [2016, 3]=>#<BigDecimal:7faef9093850,'0.46E2',9(18)>} 
Verwandte Themen