I've had trouble finding/understanding documentation on how to compare enums in Swift by their order of definition. Specifically when I create an enumeration such as
enum EnumType { case First, Second, Third }
Swift does not allow me to directly compare enums by order, such as
let type1 = EnumType.First let type2 = EnumType.Second if type1 < type2 {println("good")} // error
it generates the compile error "cannot invoke '<' with argument list of of type {EnumType, EnumType}. So the only solution I've found is to write my own comparison operators as overloads, such as
enum EnumType : Int { case First = 0, Second, Third } func <(a: EnumType, b: EnumType) -> Bool { return a.rawValue < b.rawValue } let type1 = EnumType.First let type2 = EnumType.Second if type1 < type2 {println("good")} // Returns "good"
This is all well and good for "heavy weight" enums that have a lot of use and value in my application, but overloading all the operators I might want to use seems excessively burdensome for 'lightweight" enums that I might define on the fly to bring order to some constants for a single small module.
Is there way to do this without writing lots of boilerplate overloading code for every enum type I define in my project? Even better, is there something I'm missing to make Swift automatically provide comparison operators for simple enums that don't have associated types, ie. that are untyped or typed as Int? Swift knows how to compare Ints, so why can't it compare enum Ints?
Enumeration is a data type that allows you to define a list of possible values. An enum allows you to create a data type with those set of values so that they can be recognised consistently throughout your app.
In Swift, an enum (short for enumeration) is a user-defined data type that has a fixed set of related values. We use the enum keyword to create an enum. For example, enum Season { case spring, summer, autumn, winter } Here, Season - name of the enum.
The Problem with Associated Values We had to do this because Swift doesn't allow us to have both: raw values and associated values within the same enum. A Swift enum can either have raw values or associated values.
Enumerations in Swift are much more flexible, and don't have to provide a value for each case of the enumeration. If a value (known as a raw value) is provided for each enumeration case, the value can be a string, a character, or a value of any integer or floating-point type.
So long as you give your enum an underlying type, it’ll conform to the protocol RawRepresentable
.
This means you can write a generic comparison operator for any type that is raw representable, and has a raw type that is comparable, like so:
func <<T: RawRepresentable where T.RawValue: Comparable>(a: T, b: T) -> Bool { return a.rawValue < b.rawValue }
which will mean your enum will automatically have a <
operator:
enum E: Int { // this would work with Double and String also // btw, no need to give a seed value of 0, // that happens automatically for Ints case A, B, C, D, E } E.A < E.C // returns true
The only bit of boilerplate you’ll still have to do is tag your enum as Comparable
in case you want to use it with generic algorithms that require that:
extension E: Comparable { } // (no need for anything else - requirements are already fulfilled) let a: [E] = [.C, .E, .A] let b = sorted(a) // b will now be [.A, .C, .E]
Making it conform to Comparable
will also give it <=
, >
, and >=
operators automatically (supplied by the standard library).
This is to some extent the same answer as the OP proposed himself. It does involve a bit of boilerplate code for every enum that you want to be comparable, but I prefer this than having some external magic function that provides comparable to all enums. That can cause problems if you do a quick copy-and-paste from one program to another, and then the enum doesn't work and you can't remember why.
public enum LogLevel: Int, Comparable { case verbose case debug case info case warning case error case severe // Implement Comparable public static func < (a: LogLevel, b: LogLevel) -> Bool { return a.rawValue < b.rawValue } }
EDIT:
This is in response to a comment by @JasonMoore.
Comparable does not require ==. That is required by Equatable, and the Swift standard library automatically provides Equatable for most kinds of enums.
http://www.jessesquires.com/blog/swift-enumerations-and-equatable/
As for >, <= and >=, the Apple documentation says that they are required by Comparable, but that a default implementation is provided (based on use of == and <, I assume).
https://developer.apple.com/documentation/swift/comparable
Here's a bit of code that I ran in the IBM Swift Sandbox - it compiles and runs fine with the above definition.
let a : LogLevel = LogLevel.verbose let b : LogLevel = LogLevel.verbose let c : LogLevel = LogLevel.warning print(a == b) // prints true print(a > c) // prints false print(a <= c) // prints true
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With