I'm trying to create a script to go through an index, look at each page number, and tell me what chapter of the book that entry is in. Here's an approximation of what I'm doing:
@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)
will spit out the string I want, but find_chapter(15)
doesn't return anything. Is it not possible to use a range as a key like this?
A Hash is a dictionary-like collection of unique keys and their values. Also called associative arrays, they are similar to Arrays, but where an Array uses integers as its index, a Hash allows you to use any object type. Hashes enumerate their values in the order that the corresponding keys were inserted.
Each key can only have one value. But the same value can occur more than once inside a Hash, while each key can occur only once.
Ruby hash creation. A hash can be created in two basic ways: with the new keyword or with the hash literal. The first script creates a hash and adds two key-value pairs into the hash object. A hash object is created.
Multiple Values For One Key Words are unique, but they can have multiple values (definitions) associated with them. You can do this in Ruby!
You can use a range for Hash keys and you can look up keys very easily using select
like this:
@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"}
If you only want the chapter name, just add .values.first
like this:
@chapters.select {|chapter| chapter === 9 }.values.first
#=> "chapter2.xhtml"
Here's a concise way of returning just the value of the first matching key:
# 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 }
Sure, just reverse the comparison
if page_range === number
Like this
@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
It works this way because ===
method on Range has special behaviour: Range#===. If you place number
first, then Fixnum#===
is called, which compares values numerically. Range isn't a number, so they don't match.
As @Sergio Tulentsev demonstrates, it can be done. The usual way to do this however is by using a case when
. It is a bit more flexible because you can execute code in the then
clause and you can use an else
part handling everything unhandled . It uses the same ===
method under the hood.
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With