Lesser than or greater than in Swift switch statement
Here's one approach. Assuming someVar
is an Int
or other Comparable
, you can optionally assign the operand to a new variable. This lets you scope it however you want using the where
keyword:
var someVar = 3
switch someVar {
case let x where x < 0:
print("x is \(x)")
case let x where x == 0:
print("x is \(x)")
case let x where x > 0:
print("x is \(x)")
default:
print("this is impossible")
}
This can be simplified a bit:
switch someVar {
case _ where someVar < 0:
print("someVar is \(someVar)")
case 0:
print("someVar is 0")
case _ where someVar > 0:
print("someVar is \(someVar)")
default:
print("this is impossible")
}
You can also avoid the where
keyword entirely with range matching:
switch someVar {
case Int.min..<0:
print("someVar is \(someVar)")
case 0:
print("someVar is 0")
default:
print("someVar is \(someVar)")
}
You can:
switch true {
case someVar < 0:
print("less than zero")
case someVar == 0:
print("eq 0")
default:
print("otherwise")
}
With Swift 5, you can choose one of the following switch in order to replace your if statement.
#1 Using switch with PartialRangeFrom
and PartialRangeUpTo
let value = 1
switch value {
case 1...:
print("greater than zero")
case 0:
print("zero")
case ..<0:
print("less than zero")
default:
fatalError()
}
#2 Using switch with ClosedRange
and Range
let value = 1
switch value {
case 1 ... Int.max:
print("greater than zero")
case Int.min ..< 0:
print("less than zero")
case 0:
print("zero")
default:
fatalError()
}
#3 Using switch with where clause
let value = 1
switch value {
case let val where val > 0:
print("\(val) is greater than zero")
case let val where val == 0:
print("\(val) is zero")
case let val where val < 0:
print("\(val) is less than zero")
default:
fatalError()
}
#4 Using switch with where clause and assignment to _
let value = 1
switch value {
case _ where value > 0:
print("greater than zero")
case _ where value == 0:
print("zero")
case _ where value < 0:
print("less than zero")
default:
fatalError()
}
#5 Using switch with RangeExpression
protocol's ~=(_:_:)
operator
let value = 1
switch true {
case 1... ~= value:
print("greater than zero")
case ..<0 ~= value:
print("less than zero")
default:
print("zero")
}
#6 Using switch with Equatable
protocol's ~=(_:_:)
operator
let value = 1
switch true {
case value > 0:
print("greater than zero")
case value < 0:
print("less than zero")
case 0 ~= value:
print("zero")
default:
fatalError()
}
#7 Using switch with PartialRangeFrom
, PartialRangeUpTo
and RangeExpression
's contains(_:)
method
let value = 1
switch true {
case (1...).contains(value):
print("greater than zero")
case (..<0).contains(value):
print("less than zero")
default:
print("zero")
}
The switch
statement, under the hood, uses the ~=
operator. So this:
let x = 2
switch x {
case 1: print(1)
case 2: print(2)
case 3..<5: print(3..<5)
default: break
}
Desugars to this:
if 1 ~= x { print(1) }
else if 2 ~= x { print(2) }
else if 3..<5 ~= x { print(3..<5) }
else { }
If you look at the standard library reference, it can tell you exactly what the ~=
is overloaded to do: included is range-matching, and equating for equatable things. (Not included is enum-case matching, which is a language feature, rather than a function in the std lib)
You'll see that it doesn't match a straight boolean on the left-hand-side. For those kind of comparisons, you need to add a where statement.
Unless... you overload the ~=
operator yourself. (This is generally not recommended) One possibility would be something like this:
func ~= <T> (lhs: T -> Bool, rhs: T) -> Bool {
return lhs(rhs)
}
So that matches a function that returns a boolean on the left to its parameter on the right. Here's the kind of thing you could use it for:
func isEven(n: Int) -> Bool { return n % 2 == 0 }
switch 2 {
case isEven: print("Even!")
default: print("Odd!")
}
For your case, you might have a statement that looks like this:
switch someVar {
case isNegative: ...
case 0: ...
case isPositive: ...
}
But now you have to define new isNegative
and isPositive
functions. Unless you overload some more operators...
You can overload normal infix operators to be curried prefix or postfix operators. Here's an example:
postfix operator < {}
postfix func < <T : Comparable>(lhs: T)(_ rhs: T) -> Bool {
return lhs < rhs
}
This would work like this:
let isGreaterThanFive = 5<
isGreaterThanFive(6) // true
isGreaterThanFive(5) // false
Combine that with the earlier function, and your switch statement can look like this:
switch someVar {
case 0< : print("Bigger than 0")
case 0 : print("0")
default : print("Less than 0")
}
Now, you probably shouldn't use this kind of thing in practice: it's a bit dodgy. You're (probably) better off sticking with the where
statement. That said, the switch statement pattern of
switch x {
case negative:
case 0:
case positive:
}
or
switch x {
case lessThan(someNumber):
case someNumber:
case greaterThan(someNumber):
}
Seems common enough for it to be worth considering.