Use "or" logic with multiple "if case" statements
Asked Answered
T

4

31

Suppose I have an enum case with an associated value, and two variables of that enum type:

enum MyEnum {
    case foo, bar(_ prop: Int)
}

let var1 = MyEnum.foo
let var2 = MyEnum.bar(1)

If I want to check if both variables match the general case with the associated value, I can do that with a comma:

if case .bar = var1, case .bar = var2 {
    print("both are bar")
}

But I need to check if either matches the case, with something like this:

if case .bar = var1 || case .bar = var2 {
    print("at least one is bar")
}

However, that doesn't compile. Is there another way to write this to make the logic work?

Truax answered 4/12, 2018 at 21:18 Comment(0)
C
17

I would resort to some sort of isBar property on the enum itself, so that the "a or b" test remains readable:

enum MyEnum {
    case foo, bar(_ prop: Int)

    var isBar: Bool {
        switch self {
        case .bar: return true
        default: return false
        }
    }
}

let var1 = MyEnum.foo
let var2 = MyEnum.bar(1)

let eitherIsBar = var1.isBar || var2.isBar
Comet answered 4/12, 2018 at 21:38 Comment(3)
I was hoping there'd be a better built-in way of doing it, but it looks like this is probably going to be the cleanest option.Truax
Why not simply var isBar: Bool { return self == .bar }Selfinduced
Because that doesn't even compileComet
B
13

My solution for this sort of thing is:

if [var1, var2].contains(.bar) {

A lot of the time I want it the other way, a single var that might be filtered based on several values:

if [.bar, .baz].contains(var)

but indeed, if there are associated values involved you have to make them equatable and define an operator.

Backstroke answered 8/4, 2019 at 15:14 Comment(0)
S
9

You have to implement Equatable protocol for your enum:

extension MyEnum: Equatable {}
func ==(lhs: MyEnum, rhs: MyEnum) -> Bool {
    switch (lhs, rhs) {
        case (let .bar(prop1), let .bar(prop2)):
            return prop1 == prop2
        case (.foo, .foo):
        return true

     default:
         return false
    }
}

Then the following code should work:

if var1 == .bar(1) || var2 == .bar(1) {
    print("at least one is bar")
}

UPD: In case if you don't need to check associated value, you can do pattern matching like this:

switch (var1, var2) {
case (.bar, _), (_, .bar): print("at least one is bar")
default: break
}
Showers answered 4/12, 2018 at 21:29 Comment(4)
This won't work well if you don't care about the associated value of case barGush
No, it won't work indeed. Updated the answer with a suggestion on how to make it work. Not as nice as if case though.Showers
Yeah, your update with the switch statement is the best general solution I could think of also.Gush
it is not suitable in a lot of casesBarbarism
D
6

My guess would be that if case and guard case are syntax sugar, and just a small feature supported by compiler to improve developer’s experience. In the first case you are using consecutive conditions, which is also just a language feature to replace && operator to make the code more readable. When you are using && or || operators, the compiler would expect to get two expressions that return boolean values. case .bar = var1 itself is not exactly an expression that could live alone, without some context in Swift, so it is not treat as an expression that returns a bool.

To summarise:

  • if case and guard case are simply syntax sugar, works together with if <expression>, <expression> syntax

  • && and || are just logical operators that are basically a functions that expect two boolean arguments on both sides.

To solve your problem, use the good old switch statement. Hope it helps.

Detraction answered 4/12, 2018 at 21:36 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.