How case works in if-case
Range Containment
Use the ~= operator, as @Alexander mentioned.
if (1...10).contains(n) {
print("pass")
}
switch n {
case 1...10: print("pass")
default: print("bug")
}
if case 1...10 = n {
print("pass")
}
if 1...10 ~= n {
print("pass")
}
You can even do Ranges of Characters:
if "a"..."z" ~= "a" {
print("pass")
}
Overloading ~=
I made a couple overloads, so that you can not only check for Range, but also Arrays and Sets
func ~=<T: Equatable> (pattern: [T], value: T) -> Bool { return pattern.contains(value) }
func ~=<T: Equatable> (pattern: Set<T>, value: T) -> Bool { return pattern.contains(value) }
func ~=<T: Equatable> (pattern: Set<T>, value: Set<T>) -> Bool { return pattern.union(value).count < pattern.count + value.count }
Which allows you to do this:
if [1, 2, 3] ~= 2 {
print("pass")
}
if [1, 2, 3] ~= [3, 4, 5] {
print("pass")
}
Tuples
However, when using tuples, you'll still have to use if case
if case (1...10, 1...10) = (number1, number1) {
print("0")
}
You can even do the _
syntactic sugar
if case (1...10, _) = (number1, number1) {
print("0")
}
Optionals
if case .some = Optional(number) {
print("pass")
}
if case .some(let x) = Optional(number) {
print(x)
}
if case let x? = Optional(number) {
print(x)
}
Just another note about these Swift operators. One must remember that Swift has a very advanced and abstracted compiler, so keywords are just keywords, and their behavior depends on their usage.
Unlike C (and other syntactically related languages from C++ to JavaScript), where if
is simply used to test a Boolean value (or something that can be converted to one), in Swift, the concept of the if
statement is much more broad. I generally think of it gating access to a scope by using a variety of techniques, including Boolean tests.
You can think of if
, guard
, while
, and repeat
-while
as more general control flow statements, with much more advanced behavior. Certainly they can still test a Boolean value, but they can also test other conditions as well. In your scenario, the condition being tested is whether some variable matches a defined pattern (does age
match the pattern 20...30
).
You can also test whether a variable was successfully set to a non-nil
value (if let
). The result of the let
operation doesn't ever return a Boolean, but since it occurs within the if
statement, the Swift runtime knows that it's part of the control flow. Also note that this slightly changes the behavior of let
, insofar as any code inside the if
block now sees the new non-nil
value and is assured it's not nil
.
These behaviors can also be combined with commas, like this:
if !skipAgeTest, // `if` tests a Boolean
let age = Double(ageString), // `if` tests optional assignment
case 20...30 = age // `if` tests pattern matching
{
// `age` variable exists and is not `nil` and is between `20` and `30`, inclusive
}
so the concept of if let
or if case
being separate operators is... not exactly thinking about it in the right way.
And like I said, this syntax is also valid in other control flows:
while !skipAgeTest,
let age = Double(ageString),
case 20...30 = age {
// `age` is validated
// Probably also change `ageString` while we're in here
}
guard !skipAgeTest,
let age = Double(ageString),
case 20...30 = age
else {
// `age` is invalid, and not available in this block
}
// `age` is valid
@matt does a good job of explaining what that code does. I'm here to suggest a better alternative.
You can use the ~=
operator to check ranges. It's a regular operator/function that just returns a Bool
, with no special language magic.
if 20...30 ~= age {
print ("in range.")
}
The operator is if case
, so you can't put parentheses. The syntax and behavior are based on those of the case
statement in a Swift switch
statement (see my online book if you need details). In a case
statement, 20...30
is an interval, used as a pattern, which operates by using contains
against the interval. The equals sign is indeed truly confusing, but that was their first attempt at a syntax for expressing what the case
statement should be comparing with (i.e. the tag that comes after the switch
keyword in a switch
statement).
So, if you understand this:
switch age {
case 20...30:
// do stuff
default:break
}
... then you understand how it is morphed directly into this:
if case 20...30 = age {
// do stuff
}