How can I compare two enums with OR operator? - swift

I need to compare if any of the two cases meets the requirement, I need to take decision else need to do something else. I tried many ways and this is one of them but every way giving error.
if case .locked = itemStatus || case .hasHistoryLocked = itemStatus {
print("YES")
} else {
print("NO")
}

A switch is common pattern for matching a series of cases. See The Swift Programming Language: Enumerations: Matching Enumeration Values with a Switch Statement.
E.g.:
switch itemStatus {
case .locked, .hasHistoryLocked:
print("YES")
default:
print("NO")
}
If you want to add this in an if or guard statement, you can wrap the above in a computed property. E.g.,
extension ItemStatus {
var isLocked: Bool {
switch self {
case .locked, .hasHistoryLocked:
return true
default:
return false
}
}
}
Then you can do things like:
func doSomethingIfUnlocked() {
guard !itemStatus.isLocked else {
return
}
// proceed with whatever you wanted if it was unlocked
}
Alternatively, you can add Equatable conformance for this type. So, imagine ItemStatus was defined like so:
enum ItemStatus {
case locked
case hasHistoryLocked
case unlocked(Int)
}
Now, if this was your type, you could just add Equatable conformance:
enum ItemStatus: Equatable {
case locked
case hasHistoryLocked
case unlocked(Int)
}
If it was not your type and you can not simply edit the original declaration, you could instead add Equatable conformance:
extension ItemStatus: Equatable {
static func == (lhs: Self, rhs: Self) -> Bool {
switch (lhs, rhs) {
case (.locked, .locked), (.hasHistoryLocked, .hasHistoryLocked): // obviously, add all cases without associated values here
return true
case (.unlocked(let lhsValue), .unlocked(let rhsValue)) where lhsValue == rhsValue: // again, add similar patterns for all cases with associated values
return true
default:
return false
}
}
}
However you add Equatable conformance to ItemStatus, you can then do things like:
func doSomethingIfUnlocked() {
guard itemStatus != .locked, itemStatus != .hasHistoryLocked else {
return
}
// proceed with whatever you wanted if it was unlocked
}

As your comment I see that you don't want to use Equatable to check enum. There is another way to check it using Enum rawValue
So it just like you get the rawValue of enum from the key and compare it with your itemStatus
enum Test : Int {
case locked = 0
case hasHistoryLocked = 1
case anything = 2
}
let itemStatus = 0
if itemStatus == Test.locked.rawValue || itemStatus == Test.hasHistoryLocked.rawValue {
print("Yes")
} else {
print("No")
}

Related

How to pass an enum case into a function that uses 'if case' to check for a property's case

I have an enum where each case has different (or none) associated values. The enum is not equatable.
I'm using on several places if case to check if a value is of a specific case.
enum MyEnum {
case justACase
case numberCase(Int)
case stringCase(String)
case objectCase(NonEquatableObject)
}
let myArray: [MyEnum] = [.justACase, .numberCase(100), .stringCase("Hello Enum"), .justACase]
if case .justACase = myArray[0] {
print("myArray[0] is .justACase")
}
if case .numberCase = myArray[1] {
print("myArray[1] is .numberCase")
}
But I would like to extract that into its own method where I pass on the case I want to check for. I imagine something like the following. (Please write in the comments if something is not clear.)
func checkCase(lhsCase: /*???*/, rhsCase: MyEnum) -> Bool {
if case lhsCase = rhsCase {
return true
} else {
return false
}
}
// Usage:
if checkCase(lhsCase: .justACase, rhsCase: myArray[0]) {
print("myArray[0] is .justACase")
}
In my example above lhsCase should not be of type MyEnum I assume, as this would mean I'd try to compare two properties (See code just below) and would require my enums to be equatables. Which they are not.
I'm not sure if this is even possible that way.
If the following would work it would also solve my problem but it does not.
if case myArray[3] = myArray[0] {
print("myArray[0] is .justACase")
}
The answer of Shadowrun is a good start. Through the CasePaths library I found EnumKit, which was partially the inspiration for CasePaths. However it gives much simpler methods to compare cases. See my implementation below.
Using this library comes with some nice bonuses, it allows enums that have associated values to be made equatable by just comparing the cases and ignoring the values. This might not always be desired but come in quite handy in a lot of cases. I use it to compare ReSwift Actions with Associated values in my tests.
import Foundation
import EnumKit
class NonEquatableObject {
}
enum MyEnum: CaseAccessible { // <-- conform to CaseAccessible
case justACase
case numberCase(Int)
case stringCase(String)
case objectCase(NonEquatableObject)
}
let myArray: [MyEnum] = [.justACase, .numberCase(100), .stringCase("Hello Enum"), .justACase]
if case .justACase = myArray[0] {
print("myArray[0] is .justACase")
}
if case .numberCase = myArray[1] {
print("myArray[1] is .numberCase")
}
func checkCase(lhsCase: MyEnum, rhsCase: MyEnum) -> Bool {
if case lhsCase = rhsCase {
return true
} else {
return false
}
}
// Usage:
if checkCase(lhsCase: .justACase, rhsCase: myArray[0]) { //<-- allows to pass variables or just the cases (unfortunately not without associated value.)
print("myArray[0] is .justACase")
}
if case myArray[3] = myArray[0] { //<-- allows this here too
print("myArray[0] is .justACase")
}
// Bonus: Adding equatable if associated values are not equatable. Looking at the case only.
extension MyEnum: Equatable {
static func == (lhs: MyEnum, rhs: MyEnum) -> Bool {
lhs.matches(case: rhs)
}
}
if myArray[3] == myArray[0] {
print("myArray[3] == myArray[0]")
}
There's a library CasePaths that might do what you want: something like this:
struct NonEO {
var a: Any
}
enum MyEnum {
case justACase
case numberCase(Int)
case stringCase(String)
case nonEO(NonEO)
}
let myArray: [MyEnum] = [.justACase, .nonEO(NonEO(a: 42)), .stringCase("Hello Enum"), .justACase, .nonEO(NonEO(a: "Thing"))]
func sameCase<T>(casePath: CasePath<MyEnum, T>,
lhs: MyEnum, rhs: MyEnum) -> Bool {
(casePath.extract(from: lhs) != nil)
&& casePath.extract(from: rhs) != nil
}
sameCase(casePath: /MyEnum.justACase, lhs: myArray[0], rhs: myArray[1]) // FALSE
sameCase(casePath: /MyEnum.justACase, lhs: myArray[0], rhs: myArray[3]) // TRUE
sameCase(casePath: /MyEnum.nonEO, lhs: myArray[1], rhs: myArray[4]) // TRUE
sameCase(casePath: /MyEnum.nonEO(.init(a: 42)), lhs: myArray[1], rhs: myArray[4]) // FALSE

Swift Pattern Matching - multiple enum-case-pattern in guard?

What's the correct syntax for using enum cases and guard to allow more-than-one case to proceed?
With a switch we can use a case-item-list to combine switch cases.
Is there anything like that for guard or if statements?
Here's a code example of kinda-sorta what I'd like to do...
enum Thingy {
case one
case two
case three
}
func doSomething(with value: Thingy) {
switch value {
case .one, .three:
print("I like these numbers")
default:
print("Two")
}
}
// Doesn't compile
func doSomethingAlt(with value: Thingy) {
guard .one, .three = value else {
print("Two")
return
}
print("I like these numbers")
}
You just need to give the conditions separated by an OR (||) condition. Here is how:
func doSomethingAlt(with value: Thingy) {
guard value == .one || value == .three else {
print("Two")
return
}
print("I like these numbers")
}
This would require for the enum to conform to Equatable. Enums without associated values or with raw-type automatically conform to Equatable. Since Swift 4.1 enum cases even with associated-types automatically conform to Equatable. Here is some code:
enum Thingy: Equatable {
case one(String)
case two
case three
}
func doSomethingAlt(with value: Thingy) {
guard value == .one("") || value == .three else {
print("Two")
return
}
print("I like these numbers")
}
And, since Swift 5.1 enum associated type can have default values. Which is an awesome feature so you just need to do this:
enum Thingy: Equatable {
case one(String = "")
case two
case three
}
func doSomethingAlt(with value: Thingy) {
guard value == .one() || value == .three else {
print("Two")
return
}
print("I like these numbers")
}

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.
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).
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)

How to look for enum in enums list?

I want to determine if an enum is present in an enums list.
Intuitively I would do this:
if myEnum == (.one || .two) { return true }
else { return false }
Of course it doesn't work.
I know that I could do:
if myEnum == .one || myEnum == .two { return true }
else { return false }
Or
if [EnumType.one, EnumType.two].contains(myEnum) { return true }
else { return false }
But I just want to be fancy today :)
I am thinking about using filter but it seems overkill.
Do you have an idea?
Thanks a lot.
Thierry
You can do
//: Playground - noun: a place where people can play
import Cocoa
enum MyEnum {
case one
case two
case three
case four
}
let list: [MyEnum] = [.one, .two, .three, .four]
if list.contains(.one) {
// Contains
} else {
// Doesn't Contain
}
If you have associated data you have to make your enum be Equatable for this to work though. For example:
//: Playground - noun: a place where people can play
import Cocoa
enum MyEnum: Equatable {
case one
case two
case three
case four
case other(String)
static func ==(lhs: MyEnum, rhs: MyEnum) -> Bool {
switch (lhs, rhs) {
case (.one, .one),
(.two, .two),
(.three, .three),
(.four, .four):
return true
case (.other(let lhsString), .other(let rhsString)) where lhsString == rhsString:
return true
default:
return false
}
}
}
let list: [MyEnum] = [.one, .two, .three, .four, .other("test")]
if list.contains(.one) {
} else {
}
I would do a switch on each one and then have a default for if you can't find either of those types.
switch myEnum {
case .one:
print("One is here baby")
case .two:
print("Two is here baby")
default:
print("Can't find the case??????")
}
That's what OptionSet are for. It's technically a struct, but in usage, look very close to enum:
struct MyOptions : OptionSet {
var rawValue: Int
init(rawValue: Int) { self.rawValue = rawValue }
static let one = MyOptions(rawValue: 1)
static let two = MyOptions(rawValue: 2)
static let three = MyOptions(rawValue: 4)
}
let option: MyOptions = [.one, .two]
if option.contains([.one, .two]) {
print("Has one and two")
}
if !option.intersection([.one, .three]).isEmpty {
print("Has one or three")
}
I would use a switch as well and group the enum cases which are handled with common logic as follows:
enum MyEnum {
case one
case two
case three
case four
}
switch myEnum {
case .one, .two:
//deal with enum cases .one and .two
default:
//deal with all other cases
}
}
If you are trying to match arbitrary strings to various cases in your enum then you can do something like this (using Swift 3).
enum CompassPoint:String {
case north
case south
case east
case west
static func has(key: String) -> Bool {
for f in iterateEnum(CompassPoint.self) {
if(f.rawValue == key) {
return true;
}
}
return false;
}
private static func iterateEnum<T: Hashable>(_: T.Type) -> AnyIterator<T> {
var i = 0
return AnyIterator {
let next = withUnsafeBytes(of: &i) { $0.load(as: T.self) }
if next.hashValue != i { return nil }
i += 1
return next
}
}
}
CompassPoint.has(key: "south") // true
CompassPoint.has(key: "something") // false
Shoutout to #rintaro for his function for iterating over enums (found below).
https://stackoverflow.com/a/28341290/5270038

How to get a case of enum in one line?

I have a enum with some case, and a array with instances of this enum, for example:
enum MyEnum {
case foo(Int)
case bar(Int)
case baz(Int)
}
let myArray = [
MyEnum.foo(1),
MyEnum.bar(2),
MyEnum.baz(3)
]
Then, I need to compare if determinate element of this array if foo, baz or baz, for example:
myArray.filter { $0 == .foo } // not work, and should need return [MyEnum.foo(1)]
I can use switch:
myArray.filter {
switch $0 {
case .foo(_):
return true
default:
return false
}
} // work, and return [MyEnum.foo(1)]
But, I want a solution less verbose, and learning more about enum.
The shortest I can think of is:
let result = myArray.filter { if case .foo = $0 { return true } else { return false } }
As mentioned before if case is a good solution since swift 2.0. But if you're going to use this same filter many times, then you will need a more expressive way to do this. For instance, you can make this enum equatable hiding your switchCase away from the viewController:
extension MyEnum: Equatable{
static func ==(lhs: MyEnum, rhs: MyEnum) -> Bool{
switch (lhs, rhs) {
case (.foo(_), .foo(_)):
return true
case (.bar(_), .bar(_)):
return true
case (.baz(_), .baz(_)):
return true
default:
return false
}
}
}
and then:
myArray.filter {
$0 == .foo(1)
}
If you really want to do it in one line, you can use reflection:
myArray.filter { Mirror(reflecting: $0).children.first!.label! == "foo" }