John Montgomery
John Montgomery

Reputation: 7096

Use "or" logic with multiple "if case" statements

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?

Upvotes: 34

Views: 17574

Answers (4)

Phill Apley
Phill Apley

Reputation: 1026

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.

Upvotes: 13

Gereon
Gereon

Reputation: 17844

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

Upvotes: 18

kovpas
kovpas

Reputation: 9593

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
}

Upvotes: 9

Witek Bobrowski
Witek Bobrowski

Reputation: 4239

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.

Upvotes: 6

Related Questions