2011-04-12 85 views
2

比方說,我有Date就像一個Ruby數組:集團通過月份和年份日期的一個Ruby數組到哈希

2011-01-20 
2011-01-23 
2011-02-01 
2011-02-15 
2011-03-21 

什麼是創建一個哈希的方便和Ruby式的方式組通過一年的日期元素,然後一個月,如:

{ 
    2011 => { 
     1 => [2011-01-20, 2011-01-23], 
     2 => [2011-02-01, 2011-02-15], 
     3 => [2011-03-21], 
    } 
} 

我可以遍歷所有提取年,月等,然後comining他們做到這一點。

Ruby爲數組和哈希提供了很多方法和塊,必須有一個更簡單的方法嗎?

+0

,除非你打算來表達他們的八進制。無論哪種方式,如果哈希是檢查的形式,則不應把'0'在哈希個月前,'0'應該不會出現。 – sawa 2011-04-12 19:03:18

+0

@sawa謝謝,我只是做這件事而忘了這件事,它不是實際的粘貼數據 – slhck 2011-04-12 19:44:36

回答

17
require 'date' 

dates = [ 
    '2011-01-20', 
    '2011-01-23', 
    '2011-02-01', 
    '2011-02-15', 
    '2011-03-21' 
].map{|sd| Date.parse(sd)} 

Hash[ 
    dates.group_by(&:year).map{|y, items| 
    [y, items.group_by{|d| d.strftime('%B')}] 
    } 
] 
#=> {2011=>{"January"=>[#<Date: 2011-01-20 (4911163/2,0,2299161)>, #<Date: 2011-01-23 (4911169/2,0,2299161)>], "February"=>[#<Date: 2011-02-01 (4911187/2,0,2299161)>, #<Date: 2011-02-15 (4911215/2,0,2299161)>], "March"=>[#<Date: 2011-03-21 (4911283/2,0,2299161)>]}} 

我注意到你已經改變月份名稱爲數字,所以你可能要用d.month或其他任何替代d.strftime('%B')

這裏有一個一步一步的解釋:

基本上只是想要兩個級別的分組:由今年第一級,第二通過一個月。 Ruby有非常有用的方法group_by,它按給定的表達式(一個塊)對元素進行分組。所以:第一部分由year分組原始數組:

hash_by_year = dates.group_by(&:year) 
# => {2011=>[#<Date: 2011-01-20 (4911163/2,0,2299161)>, #<Date: 2011-01-23 (4911169/2,0,2299161)>, #<Date: 2011-02-01 (4911187/2,0,2299161)>, #<Date: 2011-02-15 (4911215/2,0,2299161)>, #<Date: 2011-03-21 (4911283/2,0,2299161)>]} 

這給了我們第一個層次:鍵年,重視與特定年份日期的陣列。但是我們仍然需要對第二級進行分組:這就是爲什麼我們按年散列 - 按照month對其值進行分組。讓我們爲開始忘記strftime說,我們正在通過d.month分組:

hash_by_year.map{|year, dates_in_year| 
    [year, dates_in_year.group_by(&:month)] 
} 
# => [[2011, {1=>[#<Date: 2011-01-20 (4911163/2,0,2299161)>, #<Date: 2011-01-23 (4911169/2,0,2299161)>], 2=>[#<Date: 2011-02-01 (4911187/2,0,2299161)>, #<Date: 2011-02-15 (4911215/2,0,2299161)>], 3=>[#<Date: 2011-03-21 (4911283/2,0,2299161)>]}]] 

這樣,我們得到了我們的第二個層次分組。現在我們使用散列,其中的鍵是幾個月,並且給定月份的日期數組,而不是一年中的所有日期的數組。

我們唯一的問題是map返回一個數組而不是散列。這就是爲什麼我們通過Hash[]「圍繞」整個表達式,在我們的例子中,這些對使得一對散列成對,其中[year, hash_of_dates_by_month]

對不起,如果解釋聽起來令人困惑,由於嵌套,我發現很難解釋函數表達式而不是命令式。 :(

+0

看起來像最好的解決方案!因爲我對Ruby比較新,所以我會非常感激對每個功能做什麼的小解釋! – slhck 2011-04-12 19:43:37

+0

我嘗試了,但不知道解釋有多麼有用就是。 :( – 2011-04-12 20:01:47

+0

非常感謝,我認爲這是一個非常好的答案。將來可能會幫助更多的人! – slhck 2011-04-12 20:55:48

1

這也相當接近了,你只需要改變的數值月份數字爲文本月份名稱:

dates = %w(
2011-01-20 
2011-01-23 
2011-02-01 
2011-02-15 
2011-03-21 
) 

grouped = dates.inject({}) do |ret, date| 
    y,m,d = date.split('-') 
    ret[y] ||= {} 
    # Change 'm' into month name here 
    ret[y][m] ||= [] 
    ret[y][m] << date 
    ret 
end 

puts grouped.inspect 
+0

我在我的OP中大寫日期,因爲我使用Ruby的'Date'對象,而不是字符串,我會考慮它! – slhck 2011-04-12 18:42:42

+0

哦,我的錯誤,相同的注入概念應該可以工作,但是你不必調用'split'來獲取日期組件。 – ctcherry 2011-04-12 18:51:01

1
dates = %w(
2011-01-20 
2011-01-23 
2011-02-01 
2011-02-15 
2011-03-21 
) 
hash = {} 
dates.each do |date| 
    year, month = date.strftime('%Y,%B').split(',') 
    hash[year] ||= {} 
    hash[year][month] = hash[year][month].to_a << date 
end 
+0

這一年發生了什麼? – sawa 2011-04-12 19:07:40

+0

呃,對不起,錯過了 – 2011-04-12 19:07:53

相關問題