1

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.

Jordan Running
  • 102,619
  • 17
  • 182
  • 182
Wolf_Tru
  • 523
  • 7
  • 19
  • 1
    I think you're misunderstanding how case statements work; they compare the object in the case clause (in your case, `[a,b,c,d]`) to the object in each of the when clauses using the equality operator. – Hamms Jun 05 '17 at 21:19
  • 1
    note that the thing in your when clause is simply an array that's going to evaluate to `[3, 2, 5, 8]`, not something that's magically going to lazily match to a variety of arrays – Hamms Jun 05 '17 at 21:20
  • Your comment cleared things up for me. I thought `when` functioned like if else statements. So what your saying is it is really just doing `case object` == `when object`? If so it's interesting that `||` doesn't throw some kind of syntax error. I think thats why I thought it was possible. – Wolf_Tru Jun 05 '17 at 21:36
  • 2
    the or operator doesn't throw a syntax error because that's perfectly valid syntax; `3 || 5` simply evaluates to `3`. https://stackoverflow.com/a/1554379/1810460 – Hamms Jun 05 '17 at 21:38

2 Answers2

2

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]
Gerry
  • 10,337
  • 3
  • 31
  • 40
0

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"
Cary Swoveland
  • 106,649
  • 6
  • 63
  • 100