简体   繁体   中英

Ruby: Two conditions in Case statement

How do I use a case statement and check for two conditions?

I need to check that an item is an array and only contains string classes.

a = ['one','twon']  # => ["one", "twon"]

case a                                                                         # => 
when String
  puts "string
when Array && a.all?{|obj| obj.is_a?(String) }
   puts 'an array with only string classes'
else
  raise ArgumentError, "unsupported object #{obj}:#{obj.class}"
end

The above code returns the else case, when it should be returning the "an array with only string classes" statement

The conditions in a case statement are not Boolean expressions; they are values that are compared against the target object using the === operator. Of course, any class can override that operator, but the predefined one only returns true for a small set of conditions (like is_a? , equals, is contained in, =~ ...). Having a Boolean expression as the when condition doesn't trigger a match when that expression is true; it triggers a match when the target object === the value of the expression, and for most objects, obj === true is not itself true.

So you might want to swap out your case for an if / else chain in this instance:

if a.is_a? String
  puts 'string'
elsif a.is_a?(Array) && a.all? { |obj| obj.is_a? String }
  puts 'an array with only string classes'
else
  raise ArgumentError, "unsupported object #{a}:#{a.class}"
end

Or do a nested check inside the case condition. But then you would have two else states, one for the if (meaning it matched the array case but wasn't all strings) and one for the case (meaning it was something else entirely). So rather than duplicate the error-handling code like that, I used a boolean variable to indicate when an error needed to be raise d:

ok = false
case a
 when String
  puts 'string'
  ok = true
 when Array
  if a.all? { |obj| obj.is_a? String }
    puts 'an array with only string classes'
    ok = true
  end
end
if !ok 
  raise ArgumentError, "unsupported object #{a}:#{a.class}"
end

Use pattern-matching!

a = ['one','twon']

case a
in String
  puts 'string'
in Array if a.all?(String)
  puts 'an array with only string classes'
else
  raise ArgumentError, "unsupported object #{a}:#{a.class}"
end

* ruby 2.7+
** experimental feature

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM