Ruby range: operators in case statement
It should just work like you said. The below case
construct includes the value 500.
case my_number
# between 100 and 500
when 100..500
puts "Correct, do something"
end
So:
case 500
when 100..500
puts "Yep"
end
will return Yep
Or would you like to perform a separate action if the value is exactly 500?
Meaning of Ruby === operator on Ranges
1..10
indicates a Range
from 0 to 10 in the mathematical sense and hence contains 3.14259
It is not the same as [1,2,3,4,5,6,7,8,9,10]
.
This array is a consequence of the method Range#each
, used by Enumerable#to_a
to construct the array representation of an object, yielding only the integer values included
in the Range, since yielding all real values would mean traversing an infinite number of elements.
How to write a switch statement in Ruby
Ruby uses the case
expression instead.
case x
when 1..5
"It's between 1 and 5"
when 6
"It's 6"
when "foo", "bar"
"It's either foo or bar"
when String
"You passed a string"
else
"You gave me #{x} -- I have no idea what to do with that."
end
Ruby compares the object in the when
clause with the object in the case
clause using the ===
operator. For example, 1..5 === x
, and not x === 1..5
.
This allows for sophisticated when
clauses as seen above. Ranges, classes and all sorts of things can be tested for rather than just equality.
Unlike switch
statements in many other languages, Ruby’s case
does not have fall-through, so there is no need to end each when
with a break
. You can also specify multiple matches in a single when
clause like when "foo", "bar"
.
Why is the string '3' not matched in a case statement with the range ('0'...'10')?
Ranges use cover?
for case equality. So it is comparing '3' >= '0' && '3' < '10'
which results in false
because '3' < '10' #=> false
. Strings are compared based on character values.
For a better understanding you might want to see a string as an array of characters:
['3'] <=> ['1', '0'] #=> 1 (first operand is larger than the second)
To solve the issue convert your case input to an integer and use integer ranges:
case 3 # or variable.to_i
when 0...10
puts 'number is valid'
else
puts 'number is invalid'
end
This works because integers are not compared based on character code, but on actual value. 3 >= 0 && 3 < 10
results in true
.
Alternatively you could explicitly tell when to use the member?
(or include?
) method, by not passing a range, but a method instead.
case '3'
when ('0'...'10').method(:member?)
puts 'number is valid'
else
puts 'number is invalid'
end
ruby case statement with comparison
In case..when
block you can't perform any comparisons except ===
. So I'd write your code as below :
def get_price_rank(price)
case price
when 41..50
'Sorta cheap'
when 50..60
'Reasonable'
when 60..70
'Not cheap'
when 70..80
'Spendy'
when 80..90
'Expensive!'
else
if price >= 90
'Rich!'
elsif price <= 40
'Cheap!'
end
end
end
return
is implicit, thus no need to mention.
Can I use comparison between two variables inside a Ruby case statement?
IMO, you should use if
instead of case
in this case. If you'd like to use case
, however, the code might look like this:
def end_game_message(player_score, bank_score)
case player_score
when 21
"Black Jack!"
when -> s { s > bank_score }
"You win!"
when -> s { s > 21 }
"You lose!"
when -> s { s < bank_score }
"You lose"
when -> s { s == bank_score }
"Push"
end
end
puts end_game_message(21, 15)
The key point is to give Proc
object to when
clause. In this case, s
is player_score
(value given to case
clause`).
(And minor improvement: case
statement returns value so you don't have to assign a message to local variable)
Using 'case' with multiple conditions
You have two problems with your code. First of all, this:
[1..3 && 1]
is an array with one element. Since ..
has lower precedence than &&
, you're really writing 1..(3 && 1)
which is just a complicated way of saying 1..1
. That means that your case
is really:
case[roll1, roll2]
when [1..1]
"Low / Low"
when [4..1]
"High / Low"
when [1..2]
"Low / High"
when [4..2]
"JACKPOT!!"
end
The second problem is that Array
doesn't override the ===
operator that case
uses so you'll be using Object#===
which is just an alias for Object#==
. This means that your case
is equivalent to:
if([roll1, roll2] == [1..1])
"Low / Low"
elsif([roll1, roll2] == [4..1])
"High / Low"
elsif([roll1, roll2] == [1..2])
"Low / High"
elsif([roll1, roll2] == [4..2])
"JACKPOT!!"
end
[roll1, roll2]
will never equal [some_range]
because Array#==
compares element by element and roll1
will never ==
a range; furthermore, you're also comparing arrays with different sizes.
All that means that you have a complicated way of saying:
result = nil
I'd probably just use an if
for this:
result = if (1..3).include?(roll1) && roll2 == 1
'Low / Low'
elsif (4..6).include?(roll1) && roll2 == 1
'High / Low'
...
or you could use ===
explicitly:
result = if (1..3) === roll1 && roll2 == 1
'Low / Low'
elsif (4..6) === roll1 && roll2 == 1
'High / Low'
...
but again, watch out for the low precedence of ..
.
Ruby shortcut for if (number in range) then...
if (3..9).include? x
# whatever
end
As a sidenote, you can also use the triple equals operator for ranges:
if (3..9) === x
# whatever
end
This lets you use them in case statements as well:
case x
when 3..9
# Do something
when 10..17
# Do something else
end
Related Topics
Ruby Parenthesis Syntax Exception with I++ ++I
How Does This Block Work for Integer Times Method
Mini_Magick Gem Doesn't Work with My Imagemagick Install
How to Iterate Through This JSON Document Using Ruby
Any Standard Guide for Ruby Win32Ole API
Rails 6: Only One Profile Per User Should Be Created
Quote All Fields in CSV Output
Rails Object#Blank? VS. String#Empty? Confusion
How Can Same Program in Ruby Accept Input from User as Well as Command Line Arguments
Activerecord Is Not Aware of Timezones
Regexp Search Through a Very Large File
Sorting Numeric Strings in Ruby
Ruby Soap4R Wsdl2Ruby.Rb Errors
Error in Install Ruby-Debug-Base on Ruby 2.0.0P247
Validating Date Format Using Regular Expression