Wolf_Tru
Wolf_Tru

Reputation: 563

Case statements: multiple variables with multiple possibilities for each variable

At first I thought this syntax worked but it seems when will only check the first value of the groups, if any of the first values fail it will return locked. to bad the or operand doesn't work here.

def lock(a,b,c,d)
  case [a,b,c,d]
    when[(3||5||7), 2, (5||6), (8||9||0)]
      "unlocked"
    else
      "locked"
  end
end

lock(3, 2, 5, 8)
lock(5, 2, 5, 0)
lock(5, 2, 6, 8)
lock(7, 2, 5, 8)
lock(7, 2, 6, 9)

I could do an if else statement for each variable, but i was hoping there was a way to do a case statement without having to make multiple when statements.

Upvotes: 1

Views: 1487

Answers (2)

Cary Swoveland
Cary Swoveland

Reputation: 110685

As has been explained by others, this problem does not lend itself to the use of a case statement.

Since the variables appear to be digits, you could convert them to strings and use a regular expression.

def lock(entry, valid)
  r = /#{valid.map { |a| '['+a.join('|')+']' }.join }/
  entry.join.match?(r) ? 'unlocked' : 'locked'  
end

Suppose

valid = [[3, 5, 7], [2], [5, 6], [8, 9, 0]]

We compute the following regular expression for this value of valid:

r #=> /[3|5|7][2][5|6][8|9|0]/

Try it:

lock([3, 2, 5, 8], valid) #=> "unlocked"
lock([5, 2, 5, 0], valid) #=> "unlocked"
lock([5, 2, 6, 8], valid  #=> "unlocked"
lock([7, 2, 5, 8], valid) #=> "unlocked"
lock([7, 2, 6, 9], valid) #=> "unlocked"
lock([5, 2, 4, 0], valid) #=> "locked"

Upvotes: 0

Gerry
Gerry

Reputation: 10497

I'll opt to loop the array instead of using a case statement, like this:

def lock(a,b,c,d)
  combination = [[3,5,7], [2], [5,6], [8,9,0]]
  attempt     = [a,b,c,d]

  combination.each_with_index do |position, i|
    return "locked" unless position.include?(attempt[i])
  end

  "unlocked"
end

Outputs:

lock(3, 2, 5, 8)
#=> "unlocked"

lock(5, 2, 5, 0)
#=> "unlocked"

lock(5, 2, 6, 8)
#=> "unlocked"

lock(7, 2, 5, 8)
#=> "unlocked"

lock(7, 2, 6, 9)
#=> "unlocked"

lock(1, 2, 3, 4)
#=> "locked"

Why your solution fails?

Just as Hamms pointed out in his comment, the when with [(3||5||7), 2, (5||6), (8||9||0)] evaluates to [3, 2, 5, 8]. That is because each expression in parenthesis is evaluated first, so, breaking it down, it would be:

(3 || 5 || 7)
#=> 3

2
#=> 2

(5 || 6)
#=> 5

(8 || 9 || 0)
#=> 8

This is because || is evaluating if value is truthy, that is, is neither nil nor false. As soon as the expression gets to a truthy value, it will return that value and look no further. So any number will evaluate as truthy, and you will always get the first number of each expression as a result.

Back to your case statement, it is the exact same thing as writing it like:

case [a,b,c,d]
when [3, 2, 5, 8]
  "unlocked"
else
  "locked"
end

Now consider that a case statement will evaluate if the object in case is equal with the one in each when. So in, your case will be something like:

[a,b,c,d] === [3, 2, 5, 8]

Which will return true (and "unlocked") only when you call lock(3, 2, 5, 8).

Also consider that you could use multiple values with when, so using something like this will work:

case [a,b,c,d]
when [3, 2, 5, 8], [5, 2, 5, 0] then "unlocked"
else "locked"
end

In which when will be equivalent to doing:

[a,b,c,d] === [3, 2, 5, 8] || [5, 2, 5, 0]

Upvotes: 2

Related Questions