Noah Wilder
Noah Wilder

Reputation: 1574

Using Switch Statement with Struct with Nested Enum - Swift

Switch Statement with Struct with Nested Enum

Swift 4.1, Xcode 9.3.1

I have a very specific case that I need to meet, but I am having trouble writing it (more information below).

***Check comments in countHand(_:)


My Code:

enum Suit {
    case Clubs, Diamonds, Hearts, Spades
}

enum Rank {
    case Jack, Queen, King, Ace
    case Num(Int)
}

struct Card {
    let suit: Suit
    let rank: Rank
}

extension Rank: Equatable {
    static func == (lhs: Rank, rhs: Rank)
        -> Bool {
        switch (lhs, rhs) {
        case (.Jack, .Jack), (.Queen, .Queen), (.King, .King), (.Ace, .Ace):
            return true
        case let (.Num(n1), .Num(n2)) where n1 == n2:
            return true
        default:
            return false
        }
    }
}

extension Suit: Equatable {
    static func == (lhs: Suit, rhs: Suit)
        -> Bool {
        switch (lhs, rhs) {
        case (.Diamonds, .Diamonds), (.Hearts, .Hearts), (.Spades, .Spades), (.Clubs, .Clubs):
            return true
        default:
            return false
        }
    }
}


//Main Function
func countHand(_ cards: [Card]) -> Int {

    var count = 0

    zip(cards, cards[1...]).forEach { card, nextCard in
        let bothCards = (card, nextCard)

        switch bothCards {
        case let (c1, c2) where c1.rank == .Num(5) && c1.suit == .Diamonds && c2.rank == .Ace:
            count += 100

        case let (c1, c2) where c1.suit == .Hearts && ![.Jack, .Queen, .King, .Ace].contains(c2.rank): //I checked the contents of the array as a workaround to determine that the rank is a numeric value 

            //Also, if c2.rank is Rank.Num(let n) where n == 3 || n == 5 || n == 7 || n == 9

            count += n * 2

        default:
            break
        }

    }

    return count


}

Card Values

The card values are as follows:


Requirements

What I would like to do is – in regards to my second case statement – I only want it to be called if the first card, c1, and the second card, c2, meet these criterion:

  1. c1.suit is .Hearts

  2. c2.rank is numeric (.Num(n)) and n is 3, 5, 7, or 9

If both of these criterion are met, I would like to increase the value of count to n * 2


My Problem

I've searched in depth to try to find a case that is similar to mine online, but unfortunately, there are none – that I could find – that matched the level of specificity that I am looking for.

I have tried to put add it to the where predicate by doing things like where ... && c2.rank = .Num(let n) the wrote an if statement within the case to only change the value of count if it is 3, 5, 7, or 9, but this yielded all kinds of errors and I don't feel that this is the best way to implement it though. This is my main problem that I am inquiring about to get fixed. If I can do this though, I am quite sure I can take out my workaround of where ... && ![.Jack, .Queen, .King, .Ace].contains(c2.rank).


Thanks everyone in advance for all suggestions, solutions, feedback, etc., I really appreciate the help!

Upvotes: 2

Views: 3387

Answers (1)

Martin R
Martin R

Reputation: 539815

You cannot do pattern matching in the where-clause, but you could switch on both properties of both cards instead:

switch (card.suit, card.rank, nextCard.suit, nextCard.rank) {
case (.Diamonds, .Num(5), _, .Ace):
    count += 100
case (.Hearts, _, _, .Num(let n)) where n >= 3 && n % 2 != 0:
    // Or: ... where [3, 5, 7, 9].contains(n):
    count += n * 2
default:
    break
}

Remark: According to the current Swift API Design Guidelines, enum properties should be lowercase:

Names of types and protocols are UpperCamelCase. Everything else is lowerCamelCase.

Upvotes: 3

Related Questions