Reputation: 21541
Ruby has a fairly powerful case..when..else
construct for when you need to match criteria against a single variable. What is the "canonical" way to match criteria against multiple variables without simply nesting case
statements?
Wrapping multiple variables in an array (like [x, y]
) and matching against it isn't equivalent, because Ruby won't apply the magical case ===
operator to the elements of the array; the operator is only applied to the array itself.
I'm going to go ahead and respond with a community-wiki answer with a (defeated) stab at this question.
Upvotes: 7
Views: 14046
Reputation: 1151
If you're working with strings, or something that converts to strings easily, you might like this solution:
case [city, country].join(", ")
when "San Jose, Costa Rica"
# ...
when "San Jose, USA"
# ...
when "Boston, USA"
# ...
else
# ...
end
Upvotes: 0
Reputation: 6299
My not battled tested solution is to call .map(&:class)
and then is the array in the when
statements
def foo(a, b)
case [a,b].map(&:class)
when [Integer, Integer]
puts "Integer"
when [String, String]
puts "String"
else
puts "otherwise"
end
end
Upvotes: 0
Reputation: 129
Since Ruby's when
keyword supports a comma separated list of values, you could use the splat *
operator. This is, of course, assuming that you're referring to a set of discrete values that are in or could become an array.
The splat operator converts a list of arguments into an array, as frequently seen in
def method_missing(method, *args, &block)
Less well known is the fact that it also performs the inverse operation - turning an array into a list of arguments.
So in this case, you could do something like
passing_grades = ['b','c']
case grade
when 'a'
puts 'great job!'
when *passing_grades
puts 'you passed'
else
puts 'you failed'
end
Upvotes: 2
Reputation: 160551
This is a simplistic way to add ===
:
class Array
def ===(other)
return false if (other.size != self.size)
other_dup = other.dup
all? do |e|
e === other_dup.shift
end
end
end
[
['foo', 3],
%w[ foo bar ],
%w[ one ],
[]
].each do |ary|
ary_type = case ary
when [String, Fixnum] then "[String, Fixnum]"
when [String, String] then "[String, String]"
when [String] then "[String]"
else
"no match"
end
puts ary_type
end
# >> [String, Fixnum]
# >> [String, String]
# >> [String]
# >> no match
Upvotes: 3
Reputation: 58589
If this pattern is common enough in your code to warrant economical expression, you can do it yourself:
class BiPartite
attr_reader :x, :y
def self.[](x, y)
BiPartite.new(x, y)
end
def initialize(x, y)
@x, @y = x, y
end
def ===(other)
x === other.x && y === other.y
end
end
....
case BiPartite[x, y]
when BiPartite[SomeType, 1..10]
puts "some_value"
when BiPartite[:some_symbol, 11..20]
puts "some_other_value"
end
Upvotes: 3
Reputation: 21541
You need to use an if..elsif..else
, and ensure that the variables you want to match against appear on the right-hand side of the ===
operator (which is what case
essentially does).
For example, if you want to match x
and y
against some criteria:
if (SomeType === x) && (1..10 === y)
some_value
elsif (:some_symbol === x) && (11..20 === y)
some_other_value
end
Upvotes: 4