Reputation: 91891
After reading How to test equality of Swift enums with associated values, I implemented the following enum:
enum CardRank {
case Number(Int)
case Jack
case Queen
case King
case Ace
}
func ==(a: CardRank, b: CardRank) -> Bool {
switch (a, b) {
case (.Number(let a), .Number(let b)) where a == b: return true
case (.Jack, .Jack): return true
case (.Queen, .Queen): return true
case (.King, .King): return true
case (.Ace, .Ace): return true
default: return false
}
}
The following code works:
let card: CardRank = CardRank.Jack
if card == CardRank.Jack {
print("You played a jack!")
} else if card == CardRank.Number(2) {
print("A two cannot be played at this time.")
}
However, this doesn't compile:
let number = CardRank.Number(5)
if number == CardRank.Number {
print("You must play a face card!")
}
... and it gives the following error message:
Binary operator '==' cannot be applied to operands of type 'CardRank' and '(Int) -> CardRank'
I'm assuming this is because it's expecting a full type and CardRank.Number
does not specify an entire type, whereas CardRank.Number(2)
did. However, in this case, I want it to match any number; not just a specific one.
Obviously I can use a switch statement, but the whole point of implementing the ==
operator was to avoid this verbose solution:
switch number {
case .Number:
print("You must play a face card!")
default:
break
}
Is there any way to compare an enum with associated values while ignoring its associated value?
Note: I realize that I could change the case in the ==
method to case (.Number, .Number): return true
, but, although it would return true correctly, my comparison would still look like its being compared to a specific number (number == CardRank.Number(2)
; where 2 is a dummy value) rather than any number (number == CardRank.Number
).
Upvotes: 136
Views: 54478
Reputation: 2947
Building on the if-case
pattern, if you need to pluck out the associated object from the enum…
enum Fruit {
case apple (AppleStruct)
case banana (BananaStruct)
}
struct AppleStruct {
func commitOriginalSin()
}
struct BananaStruct {
func slipAndFall()
}
if case .banana(let fruit) = someFruit {
fruit.slipAndFall()
}
… and in SwiftUI…
if case .banana(let fruit) = someFruit {
BananaView(fruit)
}
if case .apple(let fruit) = someFruit {
AppleView(fruit)
}
Upvotes: -1
Reputation: 4478
In Swift 2+ you can use the if-case
pattern match:
let number = CardRank.Number(5)
if case .Number = number {
// Is a number
} else {
// Something else
}
If you're looking to avoid verbosity, you might consider adding an isNumber
computed property to your enum that implements your switch statement.
Upvotes: 125
Reputation: 1
let cardRank = CardRank.Number(10)
guard let case .Number(someNumber) = cardRank else {
return
}
print(someNum)
Upvotes: -2
Reputation: 19662
extension CardRank {
func isSameCaseAs(_ other: CardRank) -> Bool {
switch (self, other) {
case (.Number, .Number),
(.Jack, .Jack),
(.Queen, .Queen),
(.King, .King),
(.Ace, .Ace):
return true
default:
return false
}
}
}
let number = CardRank.Number(1)
let otherNumber = CardRank.Number(2)
number.isSameCaseAs(otherNumber) // true
Just create an extension and ignore the associated types.
Upvotes: 3
Reputation: 1358
From Swift 5.3, you can use the Comparable Enums feature:
enum CardRank: Comparable {
case Number(Int)
case Jack
case Queen
case King
case Ace
}
let cards: [CardRank] = [
.Queen, .Number(8), .Ace, .Number(3), .King
]
print(cards.sorted())
// [.Number(3), .Number(8), .Queen, .King, .Ace]
Upvotes: -1
Reputation: 641
What I usually do to compare if two enum cases "match" no matter their associated value is:
I have a protocol Matchable
:
protocol Matchable {
static func ~= (lhs: Self, rhs: Self) -> Bool
}
Then I make enums conform to it:
extension CardRank: Matchable {
static func ~= (lhs: Self, rhs: Self) -> Bool {
switch (lhs, rhs) {
case
(.number, .number),
(.jack, .jack),
(.queen, .queen),
(.king, .king),
(.ace, .ace):
return true
default:
return false
}
}
}
let card1: CardRank = .number(1)
let card2: CardRank = .number(2)
let card3: CardRank = .jack
print(card1 ~= card2) // true
print(card1 ~= card3) // false
Upvotes: 18
Reputation: 4765
I didn't want to conform Equatable
(it didn't help me either) and I wanted to filter for other cases than a specific one, so instead of simply writing card != .Number
I had to write the following. (I adjusted my code to this question.)
enum CardRank {
...
var isNumber: Bool {
if case .Number = self { return true }
return false
}
}
So I can write not a number in a complex condition:
if something && !card.isNumber { ... }
I wish I could just write card != .Number
, but the compiler was always complaining with Type of expression is ambiguous without more context. Maybe in an upcoming swift version!
Upvotes: 5
Reputation: 41718
You don't need func ==
or Equatable
. Just use an enumeration case pattern.
let rank = CardRank.Ace
if case .Ace = rank { print("Snoopy") }
Upvotes: 2
Reputation: 1568
In Swift 4.2 Equatable
will be synthesized if all your associated values conform to Equatable
. All you need to do is add Equatable
.
enum CardRank: Equatable {
case Number(Int)
case Jack
case Queen
case King
case Ace
}
https://developer.apple.com/documentation/swift/equatable?changes=_3
Upvotes: 31
Reputation: 9362
Here's a simpler approach:
enum CardRank {
case Two
case Three
case Four
case Five
case Six
case Seven
case Eight
case Nine
case Ten
case Jack
case Queen
case King
case Ace
var isFaceCard: Bool {
return (self == Jack) || (self == Queen) || (self == King)
}
}
There's no need to overload the == operator, and checking for card type does not require confusing syntax:
let card = CardRank.Jack
if card == CardRank.Jack {
print("You played a jack")
} else if !card.isFaceCard {
print("You must play a face card!")
}
Upvotes: 5
Reputation: 13263
Unfortunately in Swift 1.x there isn't another way so you have to use switch
which isn't as elegant as Swift 2's version where you can use if case
:
if case .Number = number {
//ignore the value
}
if case .Number(let x) = number {
//without ignoring
}
Upvotes: 39