Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unexpected case behaviour in Ruby

I got a wrong value in a very simple piece (as I thought) of code:

org = 4
case org
when org <= 1
  val = 'L'
when 2..3
  val = 'M'
when org >= 4
  val = 'H'
end

puts val
=> nil

Please, don't be angry, I expect that I missed something very obvious, but I really can't figure it out. Thanks.

like image 984
S.ork Avatar asked Jul 19 '17 16:07

S.ork


2 Answers

This is a classic Ruby blunder. case has two methods of being called, one where you pass in a thing to branch based on, and one where you don't.

If you do specify an expression in the case statement then all the other conditions are evaluated and compared with ===. In this case org <= 1 evaluates down to false and org === false is obviously not true. The same goes for all other cases, they're either true or false. That means none of them match.

If you don't specify an expression then case behaves like a fancy if.

Switch case org to case and it works. You can also switch to ranges:

val =
  case org
  when 0..1
    'L'
  when 2..3
    'M'
  else
    'H'
  end

The way your original code executes is as if it were:

org = 4
if (org <= 1) === org
  val = 'L'
elsif (2..3) === org
  val = 'M'
elsif (org >= 4) === org
  val = 'H'
end

Which is not what you want.

like image 161
tadman Avatar answered Nov 14 '22 21:11

tadman


As an alternative, you could write:

letters = %w(L L M M H)
val = letters[org.clamp(0,4)]

It uses Comparable#clamp, which comes with Ruby 2.4.

For Ruby 2.3 and older, you can use :

val = letters[[0, org, 4].sort[1]]

This way, it's fine if org is smaller than 0 or larger than 4.

like image 2
Eric Duminil Avatar answered Nov 14 '22 21:11

Eric Duminil