0

A few days ago, I have asked this question.

The brilliant solution was like this:

enum MyEnum {
    case one
    case two
    case three(user: String)
 
    func isOfSameType(_ other: MyEnum) -> Bool {
        switch (self, other) {
        case (.one, .one):
            return true
        case (.two, .two):
            return true
        case (.three, .three):
            return true
        default:
            return false
        }
    }
}
 
var array: [MyEnum] = []
func add(_ value: MyEnum) {
    if array.contains(where: { value.isOfSameType($0) }) { return }
    array.append(value)
}

Now, analyzing that with cold blood, this seems to be completely black magic.

How can the switch compare things without parameters?

But now I need to check if the last element on stack is of kind three.

Something like

if array!.last.isOfType(.three) {
}

This will not work. Xcode will require paremeters from .three.

Because I am not understanding what I have so far, I cannot figure out how to do that.

Leo Dabus
  • 229,809
  • 59
  • 489
  • 571
Ronnie
  • 332
  • 2
  • 11
  • Sorry, I have incorrectly written the question. What is not working is knowing if it is of a type that require parameters, like .three? I have fixed the question. – Ronnie Feb 05 '21 at 10:52
  • 1
    Does this answer your question? [How to compare enum with associated values by ignoring its associated value in Swift?](https://stackoverflow.com/questions/31548855/how-to-compare-enum-with-associated-values-by-ignoring-its-associated-value-in-s) – Joakim Danielson Feb 05 '21 at 12:33

4 Answers4

4

Swift Enum is quite special and has many features others don't. However, it is an enum, like C-styled enum is as simple as integer number. An instance of enum is simply a value.

In your example,

enum MyEnum {
    case one
    case two
    case three(user: String)
}

Say you have an array of,

let arrayMyEnum: [MyEnum] = [.one, .two, .three(user: "Mike"), .two, .three(user: "John")]

Then, you can,

// basic approach
arrayMyEnum.contains {
    switch $0 {
    case .three(let user):
        return true
    default:
        return false
    }
}

// Using optional pattern
arrayMyEnum.contains {
    if case .three(let user) = $0 {
        return true
    } else {
        return false
    }
}

Or as you described in your comment that you do not care the wrapped value, but simply want to check if the instance is a certain case. Then you can ignore the wrapped value,

arrayMyEnum.contains {
    switch $0 {
    case .three:
        return true
    default:
        return false
    }
}

arrayMyEnum.contains {
    if case .three = $0 {
        return true
    } else {
        return false
    }
}

I guess above is what you asked for.

Notice,

Underlying, enum instance with wrapped value has a hash for comparison. Hence, if you want to perform equality using == equal operator, the compiler warns you your enum need to conforms to Hashable.

For example, if you want compare,

enum MyEnum: Hashable {
    case one
    case two
    case three(user: String)
}

MyEnum.three(user: "SomeValue") == MyEnum.three(user: "AnotherValue")

Enum of Swfit type that itself is Hashable, are implicitly hashable.

In your case, enum with wrapped value, the wrapped value need to be Hashable. As String is already Hashable, you'd only need declare the Hashable conformance.

Only when the wrapped type is not Hashable, you'd need write the == and hasher methods yourself, which in most cases very straight forward.

Readings

Language Guide #Enumerations

Language Reference #Enumeration Case Pattern, Optional Pattern.

zrfrank
  • 2,631
  • 1
  • 12
  • 18
3

You just need to make your enumeration conform to Equatable and compare the associated values for equality:

enum MyEnum: Equatable {
    case one
    case two
    case three(user: String)
}

var array: [MyEnum] = []
func add(_ value: MyEnum) {
    if array.contains(value) { return }
    array.append(value)
}

add(.one)
add(.one)
array
add(.three(user: "a"))
add(.three(user: "b"))
add(.three(user: "a"))

array[0]  // one
array[1]  // three(user: "a")
array[2]  // three(user: "b")

To check if the last element is of case .three:

if case .three = array.last {
    print(true)
}
Leo Dabus
  • 229,809
  • 59
  • 489
  • 571
1

As I understand, you wonder why your switch statement with

        case (.three, .three):
            return true

works finde, but

if array!.last.isOfType(.three) { /* ... */ }

causes a compiler error.

The problem is caused the switch syntax is a little special (pattern matching) and cannot be used in a function call.

Since it seems that you want to ignore the value for the user, you could do the following:

if case .three = array!.last { /* ... */ }

which is exactly what the switch statement does - it ignores the assigned value.

If you want to call a function, you need to pass in a concrete instance of your enum, and since the user is ignored by isOfType, using a dummy value in the following statement will work:

if array!.last.isOfSameType(.three(user:"dummy")) { /* ... */ }
Andreas Oetjen
  • 9,889
  • 1
  • 24
  • 34
0

please try this:

implement three protocol Equatable, Hashable and CustomStringConvertible

public enum MyEnum: Equatable, Hashable, CustomStringConvertible {
    case one
    case two
    case three(user: String)
    
//    CustomStringConvertible
// create unique string for each case
    public var description: String {
        switch self {
            case .one:
            return "one"
            
            case .two:
            return "two"
            
        case .three:
            return "three"
            
        }
    }
    
//    Hashable
    public func hash(into hasher: inout Hasher) {
        hasher.combine(self.description)
    }
    
//    Equatable
    public static func == (lhs: MyEnum, rhs: MyEnum) -> Bool {
        return lhs.hashValue == rhs.hashValue
    }
}

var array: [MyEnum] = []


   func add(_ value: MyEnum) {
    
    // option one
//    if array.last!.hashValue == value.hashValue {
//        return
//    }
    
    // option two
    if array.contains(value) {
        return
    }
    
    array.append(value)
}

add(.one)
add(.one)
add(.two)
add(.two)
add(.three(user: "hello"))
add(.three(user: "hi"))
for item in array {
    print(item.description)
}

result print:

one
two
three
hessam
  • 404
  • 3
  • 10