How to do if pattern matching with multiple cases? - swift

I'm searching for the syntax to do pattern matching with multiple cases in an if case statement.
The example would be this:
enum Gender {
case Male, Female, Transgender
}
let a = Gender.Male
Now I want to check, if a is .Male OR .Female. But I would like to avoid using switch for this. However the switch statement would be like this:
switch a {
case .Male, .Female:
// do something
}
Is it possible to write this with if case?
I would expect this, but it didn't work :(
if case .Male, .Female = a {
}

A simple array does the trick:
if [.Male, .Female].contains(a) {
print("Male or female")
} else {
print("Transgender")
}
I'm simply amazed at Swift's ability to infer type. Here, it gets that .Male and .Female are of type gender from a.

If you have an associated value, you can simply create a Bool variable like this
extension Gender {
var isMaleOrFemale: Bool {
switch self {
case .Male, .Female:
return true
default:
return false
}
}
And usage:
if a.isMaleOrFemale {
// Your code here
}

You should use a collection. In JavaScript I would write something like this:
if ([Gender.Male, Gender.Female].includes(actualGender))
console.log(actualGender);
Note that I have not a clue about swift, or how to do the same in that language, so here is a relevant answer in the topic: https://stackoverflow.com/a/25391725/607033 :D
EDIT: This is the Swift version:
if [.Male, .Female].contains(a) {
}

For pattern matching, what you describe will not work yet. You could do this in your case. But if it cannot be convert into a hashValue. Then this would not work either.
// Using Pattern Matching for more than one case.
if case 0...2 = a.hashValue {
print("Hello")
}
//Normal if else
if a == .Male || a == .Female {
print("Hello")
}

Related

Using Enum in if-else in Swift

I have the following for loops with if-else condition. However, it is not quite intituitve for others to read the code at a first glance. therefore I am thinking to use Enum here.
for row in 0..<result.count {
for column in 0..<result[row].count {
if column == 0 {
// add hotels here
} else if column == 1 {
// add motels here
}
}
}
I am trying to use the following enum, but I am confused how to put into if-else condition.
enum ResortColumn {
case hotel
case motel
}
If you want to compare your enum type to an integer, you need to declare it an Int and compare its rawValue.
enum ResortColumn: Int {
case hotel
case motel
}
if column == ResultColumn.hotel.rawValue { // etc.
See the second "enum Planet" example on this page, and read the rest for a full understanding of Swift enums:
https://docs.swift.org/swift-book/LanguageGuide/Enumerations.html
enum ResortColumn: Int {
case hotel
case motel
}
for row in 0 ..< result.count {
for column in 0 ..< result[row].count {
let resortColumn = ResortColumn(rawValue: column)
switch resortColumn {
case .hotel:
// add hotels here
case .motel:
// add motels here
}
}
}
This is the best what I can come up with the context you gave. In this case, it does really matter what is behind the result variable

Alternative to case Swift

Ive got an enum
enum Display {
case string(String)
case int(Int)
case blank
}
and want to check if an element in my [Display] is blank.
I can do this with a switch case
switch element {
case .blank:
print ("all is fine")
default:
return
}
so if it isn't blank we return.
I'd prefer an if statement
if element != blank
But I can't seem to do this with an Enum. Surely there's a better way?
You can use if case for that. Read here: http://fuckingifcaseletsyntax.com
Copy paste from the website for you lazy guys out there:
if case let Puppy.mastiff(droolRating, weight) = fido {
... // tell fido he's a good boy
is equivalent to:
switch fido {
case let Puppy.mastiff(droolRating, weight):
... // tell fido he's a good boy
If you are going to to this in several places in your code it might be worth adding a computed property to your enum to check .blank
var isBlank: Bool {
get {
if case Display.blank = self {
return true
}
return false
}
}
Then you can use it in a way that makes your other code cleaner
array.contains(where: {$0.isBlank})
element.isBlank

Swift Pattern Matching in Catch for Enum with Associated Values

I would like to figure out how to pattern match against an enum-with-associated-value property of an error type in a catch. Everything works as expected with an enum without associated values, but I can't seem to figure out the correct pattern for this situation.
struct MyError: Error {
enum Size {
case big, small
}
enum Solution {
case runAway
case other(String)
}
let size: Size
let solution: Solution
}
func action() {
do {
// ...
}
catch let error as MyError where error.size == .big {
// This works fine, as `size` has no associated values.
}
catch let error as MyError where error.solution == .other {
// I want to handle all cases of `Solution.other` here, regardless of the associated value.
}
catch {
// ...
}
}
The second catch pattern won't compile (as expected due to the enum with associated value). The usual way I'd accomplish this would be a if case .runAway = error.solution {...}, but integrating this in the catch pattern is the problem.
I tried many combinations of if case/let case/case let, but couldn't get this working in a single catch pattern matching statement. This feels like it should be possible given the power and flexibility of pattern matching, so I'm hoping I've just overlooked something.
Thanks for the assistance!
This feels possible, but isn't :/. What you are trying to use is an enum case pattern. According to here, an enum case pattern is only allowed in switch, if, while, guard, and for statements.
You can add an isOther property in Solution:
var isOther: Bool {
if case .other = self {
return true
} else {
return false
}
}
And then use it in the catch:
do {
// ...
}
catch let error as MyError where error.size == .big {
// ...
}
catch let error as MyError where error.solution.isOther {
// ...
}
catch {
// ...
}
There are 2 things which should be fixed in your sample:
To compare cases of an enum it should be equitable, isn't it? For such a simple enum just mark Solution as Equitable.
Default case for a catch isn't handled, so you need to add it, eg:
do {
...
}
catch let error as MyError where error.size == .big {
// This works fine, assizehas no associated values.
}
catch let error as MyError where error.solution == .runAway {
// I want to accomplish this comparison.
}
catch let error {
...
}

Is there a way to write an `if case` statement as an expression?

Consider this code:
enum Type {
case Foo(Int)
case Bar(Int)
var isBar: Bool {
if case .Bar = self {
return true
} else {
return false
}
}
}
That's gross. I would like to write something like this instead:
enum Type {
case Foo(Int)
case Bar(Int)
var isBar: Bool {
return case .Bar = self
}
}
But such a construct does not seem to exist in Swift, or I cannot find it.
Since there's data associated with each case, I don't think it's possible to implement the ~= operator (or any other helper) in a way that's equivalent to the above expression. And in any case, if case statements exist for free for all enums, and don't need to be manually implemented.
Thus my question: is there any more concise/declarative/clean/idiomatic way to implement isBar than what I have above? Or, more directly, is there any way to express if case statements as Swift expressions?
UPDATE 2:
Another workaround... Create a var that returns an Int ONLY based on the case, then use a static (or instance, I thought static looked cleaner) method to test equivalence of just the case. It won't clash with Equatable, you don't have to overload an operator (unless you want to replace the static method with one), and you also wouldn't have to create separate var isFoo, var isBar, etc.
I know you used this example to ask a more generic question (how can I use 'if case' as an expression?) but if that's not possible, this may be a valid workaround. I apologize if this treats "the symptoms" not "the problem"
enum Something{
case Foo(Int)
case Bar(Int)
static func sameCase(a: Something, b: Something) -> Bool {
return a.caseValue == b.caseValue
}
var caseValue: Int {
switch self {
case .Foo(_):
return 0
case .Bar(_):
return 1
}
}
//if necessary
var isBar: Bool {
return Something.sameCase(self, b: Something.Bar(0))
}
}
Something.sameCase(.Bar(0), b: .Foo(0)) // false
Something.sameCase(.Bar(1), b: .Foo(2)) // false
Something.sameCase(.Foo(0), b: .Foo(0)) // true
Something.sameCase(.Bar(1), b: .Bar(2)) // true
Something.Bar(0).isBar // true
Something.Bar(5).isBar // true
Something.Foo(5).isBar // false
UPDATE 1:
Ok, so this seems to work. If you overload the == operator to ignore values and return true only when both enums are the same case, you can pass any value in your isFoo method and still determine the type.
I'm assuming you will need to customize this function to accommodate the the associated values, but it seems like a step in the right direction
enum Something {
case Foo(Int)
case Bar(Int)
var isFoo: Bool {
return self == .Foo(0) // number doesn't matter here... see below
}
}
func ==(a: Something, b: Something) -> Bool {
switch (a,b) {
case (.Bar(_), .Bar(_)):
return true
case (.Foo(_), .Foo(_)):
return true
default:
return false
}
}
let oneFoo = Something.Foo(1)
let twoFoo = Something.Foo(2)
let oneBar = Something.Bar(1)
let twoBar = Something.Bar(2)
oneFoo == twoFoo // true
oneFoo == oneFoo // true
oneFoo == oneBar // false
oneFoo == twoBar // false
OLD:
You can use self and the case name to directly check which case it is, you don't have to use the case keyword. Hopefully this will work for your situation:
enum Something{
case Foo(Int)
case Bar(Int)
var isFoo: Bool {
switch self {
case Foo:
return true
case Bar:
return false
}
}
}
So, there is a neater way, but requires a 3rd-party package: CasePaths
The idea is they work similarly to KeyPaths, and they come with a / operator to trigger it. There is also a ~= operator to check if a CasePath matches an instance.
So, you can achieve something like your original example like so:
import CasePaths
enum Type {
case Foo(Int)
case Bar(Int)
var isBar: Bool {
/Self.Bar ~= self
}
}
You can also get the value:
extension Type {
/// Returns the `Int` if this is a `Bar`, otherwise `nil`.
var barValue: Int? {
(/Self.Bar).extract(from: self)
}
}
You can do several other useful things with CasePaths as well, such as extracting the Foo values in an array of Type values:
let values: [Type] = [.Foo(1), .Bar(2), .Foo(3), .Foo(4), .Bar(5)]
let foos = values.compactMap(/Type.Foo) // [1, 3, 4]
let bars = values.compactMap(/Type.Bar) // [2, 5]
I'm sure there is somewhat of a performance cost, but it may not be an issue in your context.
I have a similar wondering, and I kept searching for some work arounds about this, and landed on this page. I came up with code like this to compromise.
fileprivate enum TypePrimitive {
case foo
case bar
}
enum Type {
case foo(Int)
case bar(Int)
fileprivate var primitiveType: TypePrimitive {
switch self {
case .foo(_): return .foo
case .bar(_): return .bar
}
}
var isFoo: Bool { self.primitiveType == .foo }
var isBar: Bool { self.primitiveType == .bar }
}
I hope Apple will provide better solution by adding some features in Swift language.
Are you looking for the ? operator ?
documentation is here under the Ternary Conditional Operator title.

Mode flipping of Enum var

What’s the best way to flip between Enum state?
enum EVEN_ODD { case Even, Odd }
var __mode_bit = EVEN_ODD.Even;
for _ in 1...5 {
__mode_bit = (__mode_bit == .Even) ? .Odd : .Even
}
Could the __mode_bit?: be simplified?
Take a look at the Apple documentation on Booleans, they give an example of a Boolean typed enum: https://developer.apple.com/swift/blog/?id=8
Since you can create an enum from raw, you could toggle the value by:
let true = MyBool(rawValue: false)
Simon
My preference would be to make the enum conform to _Incrementable (which, for some reason, is underscored even though it seems like a reasonable non-internal protocol to me), and make it wrap around.
enum EvenOdd {
case Even, Odd
}
extension EvenOdd: _Incrementable {
func successor() -> EvenOdd {
return self == .Even ? .Odd : .Even
}
}
EvenOdd.Odd.successor() // == .Even
This also gives you a pre/post increment operator for free:
var bit = EvenOdd.Odd
++bit // bit now Even
++bit // bit now Odd
++bit // bit now Even etc
Enums can have the ! operator implemented for them.
enum Parity { case Even, Odd }
prefix func !(a: Parity) -> Parity {
return a == .Even ? .Odd : .Even
}
Now I can stuff like
var parity_mode = Parity.Even
parity_mode = !parity_mode // .Odd
Based on #Simon Gladman answer with reference to Boolean