2011-04-12 48 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

相关问题