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'在哈希个月前,'0'应该不会出现。 – sawa 2011-04-12 19:03:18
@sawa谢谢,我只是做这件事而忘了这件事,它不是实际的粘贴数据 – slhck 2011-04-12 19:44:36