Ruby Range: Operators in Case Statement

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



Leave a reply



Submit