2013-07-08 104 views
15

我試圖創建一個腳本要經過一個索引,看看每個頁碼,然後告訴我是什麼書,條目是在章下面是我在做什麼的近似值:是否可以使用範圍作爲Ruby中的哈希鍵?

@chapters = { 
    1 => "introduction.xhtml", 
    2..5 => "chapter1.xhtml", 
    6..10 => "chapter2.xhtml", 
    11..18 => "chapter3.xhtml", 
    19..30 => "chapter4.xhtml" } 

def find_chapter(number) 
    @chapters.each do |page_range, chapter_name| 
    if number === page_range 
     puts "<a href=\"" + chapter_name + "\page" + number.to_s + "\">" + number.to_s + </a>" 
    end 
    end 
end 

find_chapter(1)會吐出我想要的字符串,但find_chapter(15)不會返回任何東西。是否可以使用範圍作爲這樣的關鍵?

+0

你可以使用任何能正常['hash'(http://ruby-doc.org/core-2.0/Object。 html#method-i-hash)方法作爲關鍵字,並且由於這是在Object中定義的,所以您幾乎不得不自己找到不能用作關鍵字的對象。 – tadman

回答

17

您可以使用一組哈希鍵,你可以看一下鍵很容易使用select這樣的:

@chapters = { 1 => "introduction.xhtml", 2..5 => "chapter1.xhtml", 
       6..10 => "chapter2.xhtml", 11..18 => "chapter3.xhtml",           
       19..30 => "chapter4.xhtml" } 

@chapters.select {|chapter| chapter === 5 } 
#=> {2..5=>"chapter1.xhtml"} 

如果你只想要的章名,只需添加.values.first這樣的:

@chapters.select {|chapter| chapter === 9 }.values.first 
#=> "chapter2.xhtml" 
+1

我喜歡這個答案最好的 - 看起來最習慣。 +1 – jpalm

+7

嘗試'''@ chapters.detect {| k,v | k === 5} .last''',因爲與select不同,detect將在第一次匹配時停止迭代。 – genkilabs

6

當然,只是扭轉比較

if page_range === number 

像這樣

@chapters = { 
    1 => "introduction.xhtml", 
    2..5 => "chapter1.xhtml", 
    6..10 => "chapter2.xhtml", 
    11..18 => "chapter3.xhtml", 
    19..30 => "chapter4.xhtml" } 

def find_chapter(number) 
    @chapters.each do |page_range, chapter_name| 
    if page_range === number 
     puts chapter_name 
    end 
    end 
end 

find_chapter(1) 
find_chapter(15) 
# >> introduction.xhtml 
# >> chapter3.xhtml 

是這樣工作的,因爲在範圍===方法有特殊的行爲:Range#===。如果您先放置number,則調用Fixnum#===,它將數值進行比較。範圍不是數字,因此它們不匹配。

+0

從算法上講,這可能比僅將範圍擴展到引用相同值的多個鍵更慢。 – tadman

+0

@tadman:可能。這需要測量。當然,這取決於你的範圍有多大:)(擴展可能會花費你很多的RAM) –

+0

大多數人沒有數百萬本書的章節,但是你的觀點是重要的一點。 – tadman

2

正如@Sergio Tulentsev所示,可以做到。然而,通常的做法是使用case when。它更靈活一些,因爲您可以在then子句中執行代碼,並且可以使用處理未處理的所有內容的else部分。它在引擎蓋下使用相同的===方法。

def find_chapter(number) 
    title = case number 
    when 1  then "introduction.xhtml" 
    when 2..5 then "chapter1.xhtml" 
    when 6..10 then "chapter2.xhtml" 
    when 11..18 then "chapter3.xhtml" 
    when 19..30 then "chapter4.xhtml" 
    else "chapter unknown" 
    end 
    #optionally: do something with title 
end 
2

這裏是返回第一個匹配的密鑰的只是價值的簡潔的方式:

# setup 
i = 17; 
hash = { 1..10 => :a, 11..20 => :b, 21..30 => :c }; 

# find key 
hash.find { |k, v| break v if k.cover? i } 
1

在此找到一個論壇topic。他們建議

class RangedHash 
    def initialize(hash) 
    @ranges = hash 
    end 

    def [](key) 
    @ranges.each do |range, value| 
     return value if range.include?(key) 
    end 
    nil 
    end 
end 

現在你可以使用它像

ranges = RangedHash.new(
    1..10 => 'low', 
    21..30 => 'medium', 
    41..50 => 'high' 
) 
ranges[5] #=> "low" 
ranges[15] #=> nil 
ranges[25] #=> "medium" 
相關問題