Можно ли использовать диапазон в качестве ключа для хэша в Ruby?
Я пытаюсь создать script, чтобы просмотреть индекс, посмотреть номер каждой страницы и рассказать мне, в какой главе книги находится эта запись. Здесь приближается то, что я делаю:
@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)
ничего не возвращает. Невозможно использовать диапазон в качестве такого ключа?
Ответы
Ответ 1
Вы можете использовать диапазон для клавиш Hash, и вы можете легко искать клавиши с помощью 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"
Ответ 2
Конечно, просто измените сравнение
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 имеет особое поведение: Range # ===. Если сначала поместить number
, тогда вызывается Fixnum#===
, который сравнивает значения численно. Диапазон не является числом, поэтому они не совпадают.
Ответ 3
Как демонстрирует @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
Ответ 4
Вот краткая возможность возврата только значения первого совпадающего ключа:
# 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 }
Ответ 5
Найден форум на этом 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"