420

The best way I can describe what I'm looking for is to show you the failed code I've tried thus far:

case car
when ['honda', 'acura'].include?(car)
  # code
when 'toyota' || 'lexus'
  # code
end

I've got about 4 or 5 different when situations that should be triggered by approximately 50 different possible values of car. Is there a way to do this with case blocks or should I try a massive if block?

6 Answers 6

902

In a case statement, a , is the equivalent of || in an if statement.

case Expression in Ruby

case car
when 'toyota', 'lexus'
  puts 'brand is part of the Toyota Motor Corporation'
else
  puts 'some other brand'
end

# NOTE: You may use `then` after the when condition.
#       It is frequently used to place the body of the `when` on a single line.
case car
when 'toyota', 'lexus' then puts 'brand is part of the Toyota Motor Corporation'
else puts 'some other brand'
end
Sign up to request clarification or add additional context in comments.

6 Comments

I don't know why, but this strange situation happens: When I write this: when "toyota", "lexus", I get: unexpected tSTRING_BEG, expecting keyword_do or '{' or '(' (SyntaxError). However, when I write this: when "toyota","lexus", it works. The only difference is a space after comma.
@FurkanAyhan That's odd. I went ahead and tested the code just to make sure and it does work. My guess is there's something else going on in your code that's making it error like that. Is it possible you forgot to close out a string somewhere or something like that?
well, this works, but as ruby focus on programmer's ease, i wonder why it does nto support standard || or 'or'? This is kind of confusing
@ZiaUlRehmanMughal, agreed. And even worse in my case, the || was causing a quiet error so it looked like the case succeeded until I really started digging in.. Beware.
Ruby doesn't support or or || here because the when takes a series of comma-separated expressions to the right of it, not a single identifier. Because of this, if you had when a or b, it's not clear whether this is to be taken as the equivalent of when a, b or when (a or b), the latter of which evaluates the expression a or b first before throwing it into the when. It's more surprising and less easy to handle for the language to have tokens that change behavior based on context, and then you wouldn't be able to use a real or expression on the right side of a when.
|
123

You might take advantage of ruby's "splat" or flattening syntax.

This makes overgrown when clauses — you have about 10 values to test per branch if I understand correctly — a little more readable in my opinion. Additionally, you can modify the values to test at runtime. For example:

honda  = ['honda', 'acura', 'civic', 'element', 'fit', ...]
toyota = ['toyota', 'lexus', 'tercel', 'rx', 'yaris', ...]
...

if include_concept_cars
  honda += ['ev-ster', 'concept c', 'concept s', ...]
  ...
end

case car
when *toyota
  # Do something for Toyota cars
when *honda
  # Do something for Honda cars
...
end

Another common approach would be to use a hash as a dispatch table, with keys for each value of car and values that are some callable object encapsulating the code you wish to execute.

2 Comments

This is what I ended up using, though I feel bad taking away someone's check mark :D
Brilliant solution for long when lines. Thanks for sharing.
3

Remember switch/case (case/when, etc.) is just comparing values. I like the official answer in this instance for a simple or'd string list comparison, but for more exotic conditional / matching logic,

case true
  when ['honda', 'acura'].include?(car)
    # do something
  when (condition1 && (condition2 || condition3))
    # do  something different
  else
    # do something else
end

Comments

0

Another nice way to put your logic in data is something like this:

# Initialization.
CAR_TYPES = {
  foo_type: ['honda', 'acura', 'mercedes'],
  bar_type: ['toyota', 'lexus']
  # More...
}
@type_for_name = {}
CAR_TYPES.each { |type, names| names.each { |name| @type_for_name[type] = name } }

case @type_for_name[car]
when :foo_type
  # do foo things
when :bar_type
  # do bar things
end

3 Comments

I don't mean to be rude, but I downvoted because this is less efficient in both time and space. It's also more complex and less readable than the other two answers. What would be the benefit of using this method?
It puts your whole classification into one object. You can now do things with that object, such as serialize it and send it to someone else to explain your logic, or store it in a database and allow people to edit it. (The logic will change pretty soon when new car models come out, right?) You might look up "table-driven".
YAGNI ("You aren't gonna need it") may apply here. The design sacrifices time/space efficiency and readability for a scenario that may exist in the future but doesn't exist yet. The cost is paid now, but the reward may never be reaped.
0

you could do something like this (inspired by @pilcrow's answer):

honda  = %w[honda acura civic element fit ...]
toyota = %w[toyota lexus tercel rx yaris ...]

honda += %w[ev_ster concept_c concept_s ...] if include_concept_cars

case car
when *toyota
  # Do something for Toyota cars
when *honda
  # Do something for Honda cars
...
end

Comments

-3

In a case statement, equivalent of && in an if statement.

case coding_language when 'ror' && 'javascript' # code end

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.