2016-11-30 153 views
3

我需要在Ruby中按年龄分组人员。我有他们的出生日期,以及一种年复一年的方法。所以像这样的解决方案工作。询问散列的红宝石数组

case 
when (0..15).cover?(age_years) 
    'child' 
when (16..24).cover?(age_years) 
    '16 to 24' 
when (25..34).cover?(age_years) 
    '25 to 34' 
when (35..44).cover?(age_years) 
    '35 to 44' 
when (45..54).cover?(age_years) 
    '45 to 54' 
when (55..64).cover?(age_years) 
    '55 to 64' 
when age_years > 64 
    'really old' 
else 
    'unknown' 
end 

但是,我正在尝试学习Ruby,并且正在寻找更优雅的解决方案。我想过把age_ranges成这样散列数组...

age_ranges = [{ name: 'child', min_age: 0, max_age: 15 }, 
       { name: '16 to 24', min_age: 16, max_age: 24 }] 

,但我在一个不知如何询问这个数据返回正确的名称,其中的age_years是适当的范围内,或甚至一系列这样

age_ranges = [{ name: 'child', age_range: '0..15' }, 
       { name: '16 to 24', age_range: '16..24' }] 

看起来整洁,但我有,如果我写的胡言乱语,因为我不知道该如何年龄匹配年时提取的名字都不知道。

有人能指出我正确的方向吗?

回答

5

现在,你有年龄名称和范围(注意我用范围,不串作为age_range的值),你要搜索的age_ranges阵列散列这样的,它的age_range值包括的地图年龄:

def age_ranges 
    [ 
    { name: 'child', age_range: 0..15 }, 
    { name: '16 to 24', age_range: 16..24 } 
    ] 
end 

def find_age(age) 
    age_ranges.find { |hash| hash[:age_range].include?(age) }[:name] 
end 

find_age(12) 
#=> "child" 
find_age(17) 
#=> "16 to 24" 

注意,如果find回报nil(意思是,没有找到任何匹配),该[:name]将失败。

为了克服它或者添加一个无限范围的最后一个阵列中(我更喜欢这一个,因为它更简单):

def age_ranges 
    [ 
    { name: 'child', age_range: 0..15 }, 
    { name: '16 to 24', age_range: 16..24 }, 
    { name: 'unknown', age_range: 25..Float::INFINITY } 
    ] 
end 

或者处理它而获取的年龄在find_age方法:

def find_age(age) 
    age_ranges.each_with_object('unknown') { |hash, _| break hash[:name] if hash[:age_range].include?(age) } 
end 

此外,确保处理传递给方法的负数(因为age_ranges不包括底片):

def find_age(age) 
    return 'Age can not be less than 0' if age.negative? 
    age_ranges.find { |hash| hash[:age_range].include?(age) }[:name] 
end 

P.S.毕竟这些“音符/确认”我想说的是,@ mudasobwa的回答是去它:)

+0

'age_ranges.each_with_object('unknown'){| a,_ |如果[:age_range] === age}'处理“未知”情况,则破坏[:name],而您的代码不会。 – mudasobwa

+0

一般来说,'find' /'detect'不适用于'case'的代理,除非你有明确的||。 ELSE_VARIANT'。 – mudasobwa

+0

@mudasobwa实际上在看了你的代码之后,决定添加一个关于传递给方法的超出范围年龄的注释。稍后会编辑,谢谢 –

5

用最简单的方式Range#===三重等于直接,因为它是应该被使用:

case age_years 
when 0..15 then 'child' 
when 16..24 then '16 to 24' 
when 25..34 then '25 to 34' 
when 35..44 then '35 to 44' 
when 45..54 then '45 to 54' 
when 55..64 then '55 to 64' 
when 64..Float::INFINITY then 'really old' # or when 64.method(:<).to_proc 
else 'unknown' 
end 

为了case接受花车,应使用三联点范围:

case age_years 
when 0...16 then 'child' 
when 16...25 then '16 to 24' 
when 25...35 then '25 to 34' 
when 35...45 then '35 to 44' 
when 45...55 then '45 to 54' 
when 55...64 then '55 to 64' 
when 64..Float::INFINITY then 'really old' # or when 64.method(:<).to_proc 
else 'unknown' 
end 
+0

'Proc#==='也很有用:'when - >(a){a> 64}' – Stefan

+0

哈,我问了这个同样的问题[几年前](http://stackoverflow.com/q/8531662/125816):) –

+0

或者把'当零然后'未知''放在顶部,并使用'else'来处理“超过64”部分。 – Stefan

0

以下是我会做,以避免16和64之间的代码重复:

def age_range(age, offset=4, span=10, lowest_age=16) 
    i = ((age-offset-1)/span).to_i 
    min = [i*span+offset+1, lowest_age].max 
    max = (i+1)*span + offset 
    "#{min} to #{max}" 
end 

def age_description(age) 
    case age 
    when 0...16 then 'child' 
    when 16..64 then age_range(age) 
    when 64..999 then 'really old' 
    else 'unknown' 
    end 
end 

(0..99).each do |age| 
    puts "%s (%s)" % [age_description(age), age] 
end 

它输出:

child (0) 
child (1) 
child (2) 
child (3) 
child (4) 
child (5) 
child (6) 
child (7) 
child (8) 
child (9) 
child (10) 
child (11) 
child (12) 
child (13) 
child (14) 
child (15) 
16 to 24 (16) 
16 to 24 (17) 
16 to 24 (18) 
16 to 24 (19) 
16 to 24 (20) 
16 to 24 (21) 
16 to 24 (22) 
16 to 24 (23) 
16 to 24 (24) 
25 to 34 (25) 
25 to 34 (26) 
25 to 34 (27) 
25 to 34 (28) 
25 to 34 (29) 
25 to 34 (30) 
25 to 34 (31) 
25 to 34 (32) 
25 to 34 (33) 
25 to 34 (34) 
35 to 44 (35) 
... 

作为奖励,它也可与浮筒(例如15.9和16.0)。

+2

为什么你禁止人们生活超过999年?! – mudasobwa

+0

为什么你禁止人们比Aleph-naught更活? :) –

+0

@mudasobwa更为严重的一点是,为1000岁以上的人返回'unknown'可能有助于发现错误,例如将'31/12/99'解析为'0099年12月31日',并将'age'返回为1918 。 –