2013-07-08 68 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" 
相关问题