Chris
Chris

Reputation: 3827

How to check Swift nested enums for equality of the outer enum only?

I'm trying to use nested enums to describe my model, in a way that makes illegal states impossible and provides categorisation at the top level. Right now, my (simplified) code is:

enum SportsEvent {

    enum RunningEvent {
        case sprint
        case marathon
    }

    enum ThrowingEvent {
        case hammer
        case javelin
        case discus
    }

    case running(event: RunningEvent)
    case throwing(event: ThrowingEvent)

    func isSameCategory(as other: SportsEvent) -> Bool {

        return false
    }
}

let sprint = SportsEvent.running(event: .sprint)
let javelin = SportsEvent.throwing(event: .javelin)
let hammer = SportsEvent.throwing(event: .hammer)

sprint.isSameCategory(as: javelin)      // False
hammer.isSameCategory(as: javelin)      // True

It feels like it should be trivial with an if case ... and a wildcard, but I'm not seeing how to achieve that. I'm hoping a giant switch statement isn't necessary, as my actual model is more complex.

Upvotes: 1

Views: 686

Answers (2)

Sulthan
Sulthan

Reputation: 130201

Depending on your use case, you might be able to convert SportEvent to a protocol:

enum RunningEvent {
    case sprint
    case marathon
}

enum ThrowingEvent {
    case hammer
    case javelin
    case discus
}

enum SportEventCategory {
    case running
    case throwing
}

protocol SportEvent {
    var category: SportEventCategory { get }
}

extension RunningEvent: SportEvent {
    var category: SportEventCategory {
        return .running
    }
}

extension ThrowingEvent: SportEvent {
    var category: SportEventCategory {
        return .throwing
    }
}

let sportEvent1: SportEvent = RunningEvent.sprint
let sportEvent2: SportEvent = ThrowingEvent.hammer
print(sportEvent1.category == sportEvent2.category)

or even as one flat enum:

enum SportEvent {
    enum Category {
        case running
        case throwing
    }

    case sprint
    case marathon
    case hammer
    case javelin
    case discus

    var category: Category {
        switch self {
        case .sprint, .marathon, .hammer:
            return .running
        case .javelin, .discus:
            return .throwing
        }
    }
}

let sportEvent1: SportEvent = .sprint
let sportEvent2: SportEvent = .marathon
print(sportEvent1.category == sportEvent2.category)

Upvotes: 0

Martin R
Martin R

Reputation: 540075

I think you need a switch-statement, with a “compound case” listing all possible “same value combinations” of the outer enumeration, plus a default case:

func isSameCategory(as other: SportsEvent) -> Bool {
    switch (self, other) {
    case (.running, .running),
         (.throwing, .throwing):
        return true
    default:
        return false
    }
}

Or (attribution goes to @Hamish):

func isSameCategory(as other: SportsEvent) -> Bool {
    switch (self, other) {
    case (.running, .running),
         (.throwing, .throwing):
        return true
    case (.running, _),
         (.throwing, _):
        return false
    }
}

which has the advantage that the compiler checks that all cases are covered. For an enumeration with n cases that makes 2 * n cases in the switch statement (which is better than n * n if you checked all possible combinations).

Upvotes: 4

Related Questions