Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Swift Enumeration order and comparison

Tags:

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?

like image 335
SafeFastExpressive Avatar asked Jan 09 '15 21:01

SafeFastExpressive


People also ask

What is difference between enum and enumeration in Swift?

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.

What is enumerated () in Swift?

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.

Can associated values and raw values coexist in Swift enumeration?

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.

What is Rawvalue in Swift?

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.


2 Answers

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

like image 132
Airspeed Velocity Avatar answered Nov 13 '22 22:11

Airspeed Velocity


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 
like image 31
RenniePet Avatar answered Nov 14 '22 00:11

RenniePet