Case expression
The case expression is a multiway conditional. It returns the value of the last expression executed.
There are two forms of case expressions available.
Case expression like if/elsif/else expression style
n = 3
result =
case
when n == 1 # here you separate the conditional from the result expression with a newline
'one'
when n == 2 then 'two' # in one-liners you can separate with 'then'
when n == 3; 'three' # alternatively, in one-liners you can separate with a semicolon
when n < 1
'smaller than one'
else 'bigger than three'
end
# => result = 'three'
This is the equivalent if/elsif/else expression:
n = 3
result =
if n == 1
'one'
elsif n == 2 then 'two'
elsif n == 3; 'three'
elsif n < 1
'smaller than one'
else 'bigger than three'
end
# => result = 'three'
Once a when clause evaluates to true, no other when clauses are considered.
If no when clause is true and there is no else clause, then the value of the case expression is nil.
n = 4
result =
case
when n == 1 then 'one'
when n == 2 then 'two'
when n == 3 then 'three'
end
# => result = nil
A when clause can have more than one conditional (boolean) expression associated with it. These conditional expressions are separated by commas (,) or the || operator. If any of these conditional expressions evaluate to true, then the code associated with that when is executed.
n = 12
result =
case
when n == 1
'one'
when n == 2
'two'
when n == 3
'three'
when n == 4, n == 5
"between four and five"
when n == 6, n == 7, n == 8, n == 9 # separate conditional expressions with comma
"between six and nine"
when n == 10 || n == 11 || n == 12 || n == 13 # alternatively, separate conditional expressions with || operator
"between ten and thirteen"
when n < 1
'smaller than one'
else
'bigger than thirteen'
end
# => result = 'between ten and thirteen'
Case expression with predefined operand
In this form of the case expression each when clause is tested against a predefined operand. This predefined operand is an expression specified at the top of the case expression after the case keyword.
Each expression of each when clause is tested against the predefined operand using the === case equality operator.
In an abstract notation, the following definition
case <predefined operand>
when <expr1>
<code1>
when <expr2>
<code2>
when <expr3>, <expr4>
<code3>
else
<code else>
end
is effectively doing this
case
when <expr1> === <predefined operand>
<code1>
when <expr2> === <predefined operand>
<code2>
when <expr3> === <predefined operand> || <expr4> === <predefined operand>
<code3>
else
<code else>
end
In the first definition, the predefined (right-hand side) operand was factored out, this makes the notation more compact.
You can list several expressions (left-hand side) to be tested in a when clause. They need to be separated by a comma.
Sometimes the predefined operand is also called the target value.
The last example in if/elsif/else style could be expressed in the predefined operand style like this:
n = 12
result =
case n
when 1
'one'
when 2
'two'
when 3
'three'
when 4, 5
"between four and five"
when 6, 7, 8, 9 # separate expressions with comma
"between six and nine"
when 10, 11, 12, 13 # !!! no !!!, this is not allowed: when 10 || 11 || 12 || 13
"between ten and thirteen"
else
if n < 1
'smaller than one'
else
'bigger than thirteen'
end
end
# => result = 'between ten and thirteen'
As you see, this style is less verbose and less repetitive.
Please notice that the final when clause testing n < 1 needs to be included in the else clause.
What is effectively happening could be written in if/elsif/else expression like this:
n = 12
result =
if 1 === n
'one'
elsif 2 === n
'two'
elsif 3 === n
'three'
elsif 4 === n || 5 === n
"between four and five"
elsif 6 === n || 7 === n || 8 === n || 9 === n
"between six and nine"
elsif 10 === n || 11 === n || 12 === n || 13 === n
"between ten and thirteen"
else
if n < 1
'smaller than one'
else
'bigger than three'
end
end
# => result = 'between ten and thirteen'
=== case equality operator
Fixnum and Float classes define the === operator like the == operator.
But many other classes, e.g. like Range or Regexp, define the === operator with a special semantic.
Fixnum, Float
1 === 1 # => true
1.0 === 1.0 # => true
1 === 1.0 # => true
1.0 === 1 # => true
String
"hello" === "hello" # => true
"hello" === "Hello" # => false
Range
(1..10) === 5 # => true : 5 is an element of the range (1..10)
(1..10) === 20 # => false : 20 is not an element of the range (1..10)
Regular Expressions, Regexp
/^prefix/ === "prefix and so on" # => true
/yes/ === "no" # => false
/^[Cc]hapter[ ]*(\d*)/ === "chapter 78" # => true
Class
NilClass === false # => false
NilClass === nil # => true
String === "1" # => true
String === 1 # => false
Integer === "1" # => false
Integer === 1 # => true
Float === 1 # => false
Float === 1.0 # => true
Array === [] # => true
Array === {} # => false
Hash === {} # => true
Fixnum === Fixnum # => false
Class === Fixnum # => true
Class === Class # => true
Proc
lambda { |x| x % 7 == 0 } === 35 # => true
lambda { |x| x % 7 == 0 } === 36 # => false
User defined class
class MyClass
def self.===( collection )
if collection.respond_to?( :length )
collection.length == 4
else
false
end
end
end
MyClass === 4 # => false
MyClass === "abc" # => false
MyClass === "abcd" # => true
MyClass === [1,2,3] # => false
MyClass === [1,2,3,4] # => true
MyClass === :wxyz # => true
MyClass === { k1: "v1", k2: "v2", k3: "v3" } # => false
MyClass === { k1: "v1", k2: "v2", k3: "v3", k4: "v4" } # => true
Advanced Example
class MyClass
def self.===( collection )
if collection.respond_to?( :length )
collection.length == 4
else
false
end
end
end
def convert( expr )
numberstrings = [ "first", "second", "third" ]
divisible_by_7 = lambda { |x| Integer === x && x % 7 == 0 }
case expr
when [], {}, ""
"This is empty"
when 10
"This is number 10"
when 11
when (20..30)
"The number is between 20 and 30"
when divisible_by_7
"The number is divisible by 7"
when Integer
"This is a number"
when *numberstrings # use the splat operator * to expand an array into a list of values
"This is the first or the second or the third"
when MyClass
"This collection meets the spec"
when Array, Hash
"You should not use an array or a hash"
when /[Cc]hapter[ ]*(\d*)/
"This is chapter #{$1}"
when :special
"This is something special"
when String
"This is a string"
when Symbol
"This is a symbol"
else
"Could not match"
end
end
convert( 10 ) # => "This is number 10"
convert( 11 ) # => nil : for this when clause no expression defined -> method returns nil
convert( 'mystring' ) # => "This is a string"
convert( :a ) # => "This is a symbol"
convert( 23 ) # => "The number is between 20 and 30"
convert( 35 ) # => "The number is divisible by 7"
convert( 36 ) # => "This is a number"
convert( [1,2,3] ) # => "You should not use an array or a hash"
convert( [1,2,3,4] ) # => "This collection meets the spec"
convert( 'second' ) # => "This is first or second or third"
convert( 'Chapter 56' ) # => "This is chapter 56"
convert( [] ) # => "This is empty"
convert( {} ) # => "This is empty"
convert( "" ) # => "This is empty"
convert( nil ) # => "Could not match"