2016-11-30 150 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 。 –