Comparing two enum variables regardless of their associated values - swift

Consider this enum:
enum DataType {
case One (data: Int)
case Two (value: String)
}
Swift has pattern matching to compare an enum with associated values, like so:
let var1 = DataType.One(data: 123)
let var2 = DataType.One(data: 456)
if case DataType.One(data: _) = var2 {
print ("var2 is DataType.One")
}
How would one go about comparing not one variable against an enum type, but comparing the enum type of two variables? I saw a ton of similar questions, but none focused on the case where you have two variables.
What I basically want is:
if case var1 = var2 {
print ("var1 is the same enum type as var2")
}

Updated approach:
I think there's no native support for this. But you can achieve it by defining a custom operator (preferrably by using a protocol, but you can do it directly as well). Something like this:
protocol EnumTypeEquatable {
static func ~=(lhs: Self, rhs: Self) -> Bool
}
extension DataType: EnumTypeEquatable {
static func ~=(lhs: DataType, rhs: DataType) -> Bool {
switch (lhs, rhs) {
case (.one, .one),
(.two, .two):
return true
default:
return false
}
}
}
And then use it like:
let isTypeEqual = DataType.One(value: 1) ~= DataType.One(value: 2)
print (isTypeEqual) // true
Old approach:
protocol EnumTypeEquatable {
var enumCaseIdentifier: String { get }
}
extension DataType: EnumTypeEquatable {
var enumCaseIdentifier: String {
switch self {
case .one: return "ONE"
case .two: return "TWO"
}
}
}
func ~=<T>(lhs: T, rhs: T) -> Bool where T: EnumTypeEquatable {
return lhs.enumCaseIdentifier == rhs.enumCaseIdentifier
}
The older version depends on Runtime and might be provided with default enumCaseIdentifier implementation depending on String(describing: self) which is not recommended. (since String(describing: self) is working with CustromStringConvertible protocol and can be altered)

Just confirm to Equatable like below
extension DataType: Equatable {
static func == (lhs: DataType, rhs: DataType) -> Bool {
switch (lhs, rhs) {
case (.One, .Two), (.Two, .One):
return false
case (.One, .One), (.Two, .Two):
return true
}
}
}
If you don't want to implement Equatable just move content into instance method:
extension DataType{
func isSame(_ other: DataType) -> Bool {
switch (self, other) {
case (.One, .Two), (.Two, .One):
return false
case (.One, .One), (.Two, .Two):
return true
}
}
}
Use:
let isTypeEqual = DataType.One(value: 1).isSame(DataType.One(value: 2))
print (isTypeEqual) // true

This worked for me:
enum DataType {
case one (data: Int)
case two (value: String)
}
protocol EnumTypeEquatable {
static func sameType(lhs: Self, rhs: Self) -> Bool
}
extension DataType: EnumTypeEquatable {
static func sameType(lhs: DataType, rhs: DataType) -> Bool {
if let caseLhs = Mirror(reflecting: lhs).children.first?.label, let caseRhs = Mirror(reflecting: rhs).children.first?.label {
return (caseLhs == caseRhs)
} else { return false }
}
}
let isTypeEqual = DataType.sameType(lhs: .one(data: 1), rhs: .one(data: 2))
print (isTypeEqual) // true

Related

Is there a Swift-er way to refer to an enum case as a 'type' that can by type-checked?

I'm trying to develop a parser that works off of Swift enums that represent tokens. I would like to be able to validate that a token is one of a certain type (e.g. in this case a certain case of an enum). Looking at the Swift docs, a way to do this without an additional enum isn't apparent to me, and I'm not optimistic. Is the following as close I can get to a more succinct pure Swift language-level solution?
enum SymbolTokenType {
case river, lake, mountain, meadow, valley, openParen, closeParen, logicalAnd, logicalOr, logicalNot
}
enum SymbolToken {
case river (key: RiverKey?)
case lake (key: LakeKey?)
case mountain (key: MountainKey?)
case meadow (key: MeadowKey?)
case valley (key: ValleyKey?)
case openParen
case closeParen
case logicalAnd
case logicalOr
case logicalNot
func validateTokenType(validTypes: [SymbolTokenType] ) -> Bool {
switch(self) {
case .river:
return validTypes.contains(.river)
case .lake:
return validTypes.contains(.lake)
case .mountain:
return validTypes.contains(.mountain)
case .meadow:
return validTypes.contains(.meadow)
case .valley:
return validTypes.contains(.valley)
case .openParen:
return validTypes.contains(.openParen)
case .closeParen:
return validTypes.contains(.closeParen)
case .logicalAnd:
return validTypes.contains(.logicalAnd)
case .logicalOr:
return validTypes.contains(.logicalOr)
case .logicalNot:
return validTypes.contains(.logicalNot)
}
}
}
I would do like this:
enum Token {
case token1
case token2
case token3
func isValid(from container: [Token]) -> Bool {
container.contains(self)
}
}
let validTokens: [Token] = [.token1, .token2]
let testedToken2 = Token.token2
let testedToken3 = Token.token3
testedToken2.isValid(from: validTokens) // True
testedToken3.isValid(from: validTokens) // False
-- UPD --
If enum have associated values, I would do like this:
enum Token {
case token1 (key: String?)
case token2 (key: String?)
case token3 (key: String?)
func isValid(from container: [Token]) -> Bool {
container.contains(self)
}
}
extension Token: RawRepresentable, Equatable {
typealias RawValue = Int
init?(rawValue: Int) {
switch rawValue {
case 1: self = .token1(key: nil)
case 2: self = .token2(key: nil)
case 3: self = .token3(key: nil)
default: return nil
}
}
var rawValue: Int {
switch self {
case .token1: return 1
case .token2: return 2
case .token3: return 3
}
}
// Equatable
static func == (lhs: Token, rhs: Token) -> Bool {
lhs.rawValue == rhs.rawValue
}
}
Result:
let validTokens: [Token] = [.token1(key: nil), .token2(key: nil)]
let testedToken2 = Token.token2(key: "Second token")
let testedToken3 = Token.token3(key: "Third token")
testedToken2.isValid(from: validTokens) // True
testedToken3.isValid(from: validTokens) // False
You definitely don't need two enums. You just have to deal with cases with associated values a bit differently.
protocol SimilarComparable: Equatable {
func isSimilar(to other: Self) -> Bool
func isSimilar(toAny others: [Self]) -> Bool
}
extension SimilarComparable {
func isSimilar(toAny others: [Self]) -> Bool {
let result = others.first(where: { $0.isSimilar(to: self) }) != nil
print("Finding similarity to \(self) in \(others) == \(result)")
return result
}
}
enum SymbolToken: SimilarComparable {
case river (key: String?)
case lake (key: String?)
case mountain (key: String?)
case meadow (key: String?)
case valley (key: String?)
case openParen
case closeParen
case logicalAnd
case logicalOr
case logicalNot
func isSimilar(to other: SymbolToken) -> Bool {
let result: Bool
print("Finding similarit to \(self) to \(other) == ", separator: " ", terminator: "")
switch (self, other) {
case (.river, .river):
result = true
case (.lake, .lake):
result = true
case (.mountain, .mountain):
result = true
case (.meadow, .meadow):
result = true
case (.valley, .valley):
result = true
default:
result = self == other
}
print("\(result)")
return result
}
}
Usage would be like this:
_ = SymbolToken.river(key: "Hudson").isSimilar(to: .river(key: "Nile")) // true
_ = SymbolToken.lake(key: "Michigan").isSimilar(to: .lake(key: "Tahoe")) // true
_ = SymbolToken.closeParen.isSimilar(to: .closeParen) // true
_ = SymbolToken.logicalOr.isSimilar(to: .logicalOr) // true
_ = SymbolToken.logicalOr.isSimilar(to: .logicalAnd) // false
let tokens: [SymbolToken] = [
.river(key: "Hudson"),
.lake(key: "Tahoe"),
.closeParen,
.logicalOr,
]
_ = SymbolToken.logicalOr.isSimilar(toAny: tokens) // true
_ = SymbolToken.lake(key: "Michigan").isSimilar(toAny: tokens) // true

Can a Swift enum have a function/closure as a raw value?

I know an enum can have a closure as an associated value, such as:
enum SomeEnum {
case closureOne (String, Double -> Double)
case closureTwo (String, (Double, Double) -> Double)
}
But, can an enum have a closure as a raw value? For instance, does something like this work?
enum someEnum: () -> Void {
case closureOne = doSomething
case closureTwo = doSomethingElse
}
where
let doSomething = {
// Do something here.
}
let doSomethingElse {
// Do something else here.
}
It's not as straight forward, but you could use OptionSet, see this page:
Unlike enumerations, option sets provide a nonfailable init(rawValue:) initializer to convert from a raw value, because option sets don’t have an enumerated list of all possible cases. Option set values have a one-to-one correspondence with their associated raw values.
Could be something like this:
func doSomething() {}
func doSomethingElse() {}
struct MyClosures: OptionSet {
static let closureOne = MyClosures(rawValue: doSomething)
static let closureTwo = MyClosures(rawValue: doSomethingElse)
let rawValue: () -> Void
init(rawValue: #escaping () -> Void) {
self.rawValue = rawValue
}
init() {
rawValue = {}
}
mutating func formUnion(_ other: __owned MyClosures) {
// whatever makes sense for your case
}
mutating func formIntersection(_ other: MyClosures) {
// whatever makes sense for your case
}
mutating func formSymmetricDifference(_ other: __owned MyClosures) {
// whatever makes sense for your case
}
static func == (lhs: MyClosures, rhs: MyClosures) -> Bool {
// whatever makes sense for your case
return false
}
}
And so you can use it as:
let myClosures: MyClosures = [ .closureOne, .closureTwo ]
HOWEVER looking at your explanation in the comment:
So I'm trying to find the most efficient way to run a function given the state of a variable.
I think what you actually want is some sort of state machine. Some examples are available here and here

Filtering an array based on a Swift enum with an associated value - w/o mentioning the associated value

I have an enum with associated value for some of the cases:
enum Foo {
case a
case b
case c(String?)
}
I also have a struct with this enum as a variable
struct Bar {
var foo: Foo
}
I then have an array of this objects
let array:[Bar] = [Bar(foo: .a), Bar(foo: .c(nil)), Bar(foo: .c("someString"))]
I want to create a function that operates on a subset of this array, based on the cases it receives something like
func printOnly(objectsWithCase: Foo)
So far its pretty simple, but now here's the catch: for this operation I WANT TO IGNORE the associated value.
I would like to make this function be able to take .c case without mentioning an associated value, as if to say "give me the ones with .c regardless of the associated values".
I other words - I'd like to pass in something like .c(_) (this doesn't work of course) and have it return (print in this case) both Bar(foo: .c(nil)) and Bar(foo: .c("someString"))
So far, I only came up with changing the functions declaration to take the filtering closure instead of the cases like this:
func printArray(array: [Bar], condition: (Bar) -> Bool) {
let tmp = array.filter(condition)
print(tmp)
}
I'm wondering if there's a way to do this in Swift, while passing the cases and not the condition block ?
You can use the underscore as a wild card in pattern matching operations:
array.filter {
switch $0.foo {
case .a: return true // keep a
case .b: return false // reject b
case .c(_): return true // keep c, regardless of assoc. value.
}
}
While this is not technically what you ask for (I don't think there's any way to achieve this with enums), you can write a "fake" enum that contains a wildcard c that will match anything you want. This will give you the exact same syntax.
1) Replace Foo with the following
struct Foo: Equatable {
let rawValue: String
let associatedObject: String?
let isWildcard: Bool
fileprivate init(rawValue: String, associatedObject: String?, isWildcard: Bool) {
self.rawValue = rawValue
self.associatedObject = associatedObject
self.isWildcard = isWildcard
}
static var a: Foo {
return Foo(rawValue: "a", associatedObject: nil, isWildcard: false)
}
static var b: Foo {
return Foo(rawValue: "b", associatedObject: nil, isWildcard: false)
}
static var c: Foo {
return Foo(rawValue: "c", associatedObject: nil, isWildcard: true)
}
static func c(_ value: String?) -> Foo {
return Foo(rawValue: "c", associatedObject: value, isWildcard: false)
}
}
func ==(left: Foo, right: Foo) -> Bool {
// Match rawValue + associatedObject unless we have a wildcard
return (left.rawValue == right.rawValue)
&& (left.associatedObject == right.associatedObject || left.isWilcard || right.isWildcard)
}
2) Implement your printOnly function with ==
func printOnly(objects: [Bar], with match: Foo) {
objects.filter { $0.foo == match }.forEach { print($0) }
}
3) Success
printOnly(objects: array, with: .c) // [.c(nil), .c("someString")]
Discussion
The main drawback of this method, besides the additional boilerplate code, is that you are forced to create an enum value that should not be allowed. This method puts the responsibility on you to use it only as a wildcard, and not as a real enum value. It will also not guarantee you that other enum cases cannot be created, although you should be able to mitigate that by making the only initializer fileprivate.
Otherwise, this gives you exactly the same interface and features an enum would give you, you can define your cases just as before
let array = [Bar(foo: .a), Bar(foo: .c(nil)), Bar(foo: .c("Hello")]
Finally, you can still use it inside a switch, except you will always need to add a default statement.
switch Foo.c("Hello") {
case .a:
print("A")
case .b:
print("B")
case .c: // will match .c(nil) and .c("someString")
print("C")
default:
break
}
//: Playground - noun: a place where people can play
enum Foo {
case a
case b
case c(String?)
}
struct Bar {
var foo: Foo
}
let array:[Bar] = [Bar(foo: .a), Bar(foo: .c(nil)), Bar(foo: .c("someString"))]
func printArray(array: [Bar], condition: (Bar) -> Bool) {
let tmp = array.filter(condition)
print(tmp)
}
printArray(array: array) { bar in
switch bar.foo {
case .c:
return true
default:
return false
}
}
or
printArray(array: array) { bar in
if case let Foo.c = bar.foo {
return true
}
return false
}
EDIT
//: Playground - noun: a place where people can play
enum Foo: Equatable {
case a
case b
case c(String?)
}
func ==(lhs: Foo, rhs: Foo) -> Bool {
switch (lhs, rhs) {
case (.a, .a), (.b, .b), (.c, .c):
return true
default:
return false
}
}
struct Bar {
var foo: Foo
}
let array:[Bar] = [Bar(foo: .a), Bar(foo: .c(nil)), Bar(foo: .c("someString"))]
func printArray(array: [Bar], condition: (Bar) -> Bool) {
let tmp = array.filter(condition)
print(tmp)
}
func printOnly(objectsWithCase wantedCase: Foo) {
printArray(array: array) { bar in
if wantedCase == bar.foo {
return true
} else {
return false
}
}
}
printOnly(objectsWithCase:.c(nil))

Generic way to do math on protocol extensions

Goal
I want to extend basic types like Int, Double, Float... with more flexible properties and make it presentable in a chart on my app. For example, I made a chart draw that is suitable only for displaying Intbut cannot really display Float. I want to make sure when I pass arguments to this view it will display correctly.
Solution
So I made a protocol (for this example made it like this):
protocol SimplyChartable {
static func max(_ dataSet: [SimplyChartable]) -> SimplyChartable
}
And then make an extension for some types:
extension Int: SimplyChartable { }
extension Double: SimplyChartable { }
extension Float: SimplyChartable { }
and so on ...
Problem
This will be all numeric types, and whenever I pass it as numeric types to a func I need to extend all extension like this:
public static func max(_ dataSet: [SimplyChartable]) -> SimplyChartable {
return (dataSet as? [Int])?.max() ?? 0
}
But for Double func will be identical.
So for min I will end up with similar function, the same for divide, adding , some other math... There is a way to write it once and reuse for every type that extends this protocol?
I found out that:
let dataType = type(of: maxValue) /* where `maxValue` is SimplyChartable*/
Will return original type as rawValue. But output of a method type(of is a Metatype and I cannot return it from function and then add two values of this type. So for example this code will not work:
let val1 = SimplyChartable(4)
let val2 = SimplyChartable(2)
let sum = val1 + val2
And how to make it work not ending up with 3 functions like this:
let val1 = SimplyChartable(4)
let val2 = SimplyChartable(2)
let sum = (val1 as! Int) + (val2 as! Int)
Since they all numeric types why don't you use Comparable?
extension SimplyChartable {
static func max<T: Comparable>(dataSet: [T]) -> T? {
return dataSet.max()
}
static func min<T: Comparable>(dataSet: [T]) -> T? {
return dataSet.min()
}
}
extension Int: SimplyChartable { }
extension Double: SimplyChartable { }
Double.max([1.2, 1.1, 1.3]) // 1.3
Int.min([12, 11, 13]) // 11
Just my two cents worth...
This isn't exactly what you've asked for, since it doesn't let you call a static function directly from a protocol metatype. But since that, AFAIK, isn't possible in Swift currently, perhaps this would be the next best thing?
extension Sequence where Element == SimplyChartable {
func max() -> SimplyChartable {
// put your implementation here
}
}
You can then call this by just:
let arr: [SimplyChartable] = ...
let theMax = arr.max()
For your situation, it's much better to use an Array extension rather than a protocol with an array parameter.
To handle each possible type of array i.e [Int], [Double] or [Float], create a wrapper enum with associated types as follows:
public enum SimplyChartableType {
case int(Int)
case float(Float)
case double(Double)
func getValue() -> NSNumber {
switch self {
case .int(let int):
return NSNumber(value: int)
case .float(let float):
return NSNumber(value: float)
case .double(let double):
return NSNumber(value: double)
}
}
init(int: Int) {
self = SimplyChartableType.int(int)
}
init(float: Float) {
self = SimplyChartableType.float(float)
}
init(double: Double) {
self = SimplyChartableType.double(double)
}
}
You can extend Array as follows:
extension Array where Element == SimplyChartableType {
func max() -> SimplyChartableType {
switch self[0] {
case .int(_):
let arr = self.map({ $0.getValue().intValue })
return SimplyChartableType(int: arr.max()!)
case .double(_):
let arr = self.map({ $0.getValue().doubleValue })
return SimplyChartableType(double: arr.max()!)
case .float(_):
let arr = self.map({ $0.getValue().floatValue })
return SimplyChartableType(float: arr.max()!)
}
}
}
Example usage is:
var array = [SimplyChartableType.double(3),SimplyChartableType.double(2),SimplyChartableType.double(4)]
var max = array.max()
And now it's a lot easier to operate on Int, Double or Float together with:
extension SimplyChartableType: SimplyChartable {
//insert functions here
static func randomFunction() -> SimplyChartableType {
//perform logic here
}
}
The above snippet is good if you need a different functionality which operates on non-Collection types.
This doesn't answer your specific question, unfortunately. Perhaps a work around to use a free function and casting.
import UIKit
protocol SimplyChartable {
func chartableValue() -> Double
}
extension Int: SimplyChartable {
func chartableValue() -> Double {
return Double(self) ?? 0
}
}
extension Double: SimplyChartable {
func chartableValue() -> Double {
return self
}
}
extension Float: SimplyChartable {
func chartableValue() -> Double {
return Double(self) ?? 0
}
}
func maxOfSimplyChartables(_ dataSet: [SimplyChartable]) -> SimplyChartable {
return dataSet.max(by: { (lhs, rhs) -> Bool in
return lhs.chartableValue() < rhs.chartableValue()
}) ?? 0
}
let chartableItem1: SimplyChartable = 1255555.4
let chartableItem2: SimplyChartable = 24422
let chartableItem3: SimplyChartable = 35555
let simplyChartableValues = [chartableItem1, chartableItem2, chartableItem3]
maxOfSimplyChartables(simplyChartableValues)

access swift enum case outside of switch?

Let's say I have a simple Pet enum:
enum Pet {
case dog(name:String)
case cat(name:String)
var name:String {
switch self {
case .dog(let name):
return name
case .cat(let name):
return name
}
}
}
(The fact that they have the same type of associated value and therefore feels redundant should be ignored)
Now I want to be able to equate pets. I could write:
extension Pet:Equatable { }
func == (a:Pet, b:Pet) -> Bool {
return a.name == b.name
}
But this will allow a cat named "Spots" to be equal to a dog named "Spots". This I don't want. Is there an easy way to access the case of an enum instance? We usually do them in switch statements, but in this case, I wanted something more direct (one line), I might need to use it in a future Comparable extension after all.
I could do my own:
extension Pet {
var petType:Int {
switch self {
case .dog:
return 1
case .cat:
return 2
}
}
}
Now I can change my == implementation to be
func == (a:Pet, b:Pet) -> Bool {
return a.petType == b.petType && a.name == b.name
}
Is there some built in Swift syntax sugar that would do the same thing for me? I tried type(of:), but that just returns Pet, nothing about the instance case.
As far as I know, Swift does not provide us a shortcut to retrieve only case labels.
In your case you can write something like this utilizing your name property:
extension Pet: Equatable {
static func == (a: Pet, b: Pet) -> Bool {
switch (a, b) {
case (.dog, .dog), (.cat, .cat):
return a.name == b.name
default:
return false
}
}
}
If your Pet does not have the name property, you can write it as:
extension Pet: Equatable {
static func == (a: Pet, b: Pet) -> Bool {
switch (a, b) {
case let (.dog(namea), .dog(nameb)),
let (.cat(namea), .cat(nameb)):
return namea == nameb
default:
return false
}
}
}
But in your case, isn't it sort of natural to use class hierarchy?:
class Pet {
var name: String
init(name: String) {
self.name = name
}
}
class Dog: Pet {}
class Cat: Pet {}
extension Pet {
static func dog(name: String) -> Pet {
return Dog(name: name)
}
static func cat(name: String) -> Pet {
return Cat(name: name)
}
}
extension Pet: Equatable {
static func == (a: Pet, b: Pet) -> Bool {
return type(of: a) === type(of: b) && a.name == b.name
}
}
I have sort of a hybrid approach that keeps things a tad more modular:
enum Pet {
case dog(name:String)
case cat(name:String)
var name: String {
switch self {
case .dog(let name):
return name
case .cat(let name):
return name
}
}
func equalCaseTo(otherPet: Pet) -> Bool {
switch (self, otherPet) {
case (.dog(_), .dog(_)), (.cat(_), .cat(_)):
return true
default:
return false
}
}
func equalNameTo(otherPet: Pet) -> Bool {
return name == otherPet.name
}
}
func ==(left:Pet, right:Pet) -> Bool {
return left.equalCaseTo(right) && left.equalNameTo(right)
}
You can do something like this:
func ==(lhs: Pet, rhs: Pet) -> Bool {
if case let Pet.cat(l) = lhs, case let Pet.cat(r) = rhs where l == r { return true }
if case let Pet.dog(l) = lhs, case let Pet.dog(r) = rhs where l == r { return true }
return false
}
But a switch probably looks better and is definitely easier to maintain.