Enum pattern matching as a parameter to a function call - swift

I've setup a playground with an example:
enum CarType : Equatable {
case wheeled(wheels: Int)
case flying
public static func ==(lhs: CarType, rhs: CarType) -> Bool {
return lhs.enumName == rhs.enumName
}
var enumName: String {
let stuff = "\(self)".split(separator: "(").first!
return String(describing: stuff)
}
}
var typesPresentAtMyParty = [CarType.wheeled(wheels:4), .wheeled(wheels:4), .flying]
let aKnownType = CarType.flying
if case aKnownType = typesPresentAtMyParty[2] {
print("Was the type")
}
func isPresent(type: CarType, inArray: [CarType]) -> Bool {
return inArray.filter {
if case type = $0 {
return true
}
return false
}.first != nil
}
func isWheeled(inArray: [CarType]) -> Bool {
return inArray.filter {
if case .wheeled = $0 {
return true
}
return false
}.first != nil
}
isPresent(type: .flying, inArray: typesPresentAtMyParty)
isPresent(type: .wheeled, inArray: typesPresentAtMyParty)
The last line here does not compile. While i can do if case .wheeled = $0 ignoring associated type as a check, i cannot find a way of doing the same in a function call isPresent(type: CarType, inArray: [CarType]), when sending isPresent(type: .wheeled, inArray: typesPresentAtMyParty)
Is there a way of writing a function that takes only the valid pattern matching part of the enum as a parameter?

It is not possible to pass partially constructed enums to a function. Partially constructed enums are not valid values, and they only work in pattern matching because the compiler has a concrete value to work with - the one from the right side of the pattern.
These being said, you could easily rewrite your functions to better, more swiftier versions.
Firstly, you don't need isPresent, you can simply use contains:
typesPresentAtMyParty.contains { $0 == .flying }
typesPresentAtMyParty.contains { if case . wheeled = $0 { return true } else { return false } }
Similarly, isWheeled can be shortened (and renamed, for better semantics):
func isWheeled(_ carType: CarType) -> Bool {
if case . wheeled = carType { return true } else { return false }
}
which can pe passed to contains:
let hasWeeled = typesPresentAtMyParty.contains(where: isWheeled)

Related

How can I compare two enums with OR operator?

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")
}

Storing Generic Objects in a Heterogeneous Array and retrieving object parameter as the correct type

Ahoy everyone,
I have recently been trying to implement a Node based graph system that passes data between nodes using plugs. Similar to many of the 3D applications like houdini and maya.
I have written a similar system before using Python, and wanted to try this with Swift as my first learning excersise. Boy did I jump into the deep end on this one.
I am stuck now with Swifts Arrays, as I would like to store a list of Generic plugs.
Each plug can have its own value type float, int, color, string, Vector Matrix.
I have read up about Type Erasers and opaque types, but still cant seem to get my values our of a list in a way that I can perform some arithmetic on them.
All and any help that might put me in the direction would be greatly appreciated :D
import Foundation
import MetalKit
protocol genericPlug {
associatedtype T
func GetValue() -> T
}
class Plug<T>:genericPlug{
var _value:T?
var value:T {
get{GetValue()}
set(val){
value = val
}
}
func GetValue() -> T{
return _value!
}
init(_ newValue:T){
_value=newValue
}
}
class Node{
var plugs:[genericPlug] = []
init(){
var p1 = Plug<Int>(0)
var p2 = Plug(vector2(1.2, 3.1))
var p3 = Plug([0.0, 3.1, 0.6, 1])
plugs.append(p1)
plugs.append(p2)
plugs.append(p3)
}
func execute(){
// will access the plugs in the array and perform some sort of calculations on them.
plugs[0].value + 1 // should equal 1
plugs[1].value.x + 0.8 // should have x=2.0 y=3.1
plugs[2].value[1] - 0.1 // should equal 3.0
}
}
Thanks everyone
Use a generic something to extract what you need. Your options are methods and subscripts.
protocol PlugValue {
init()
}
extension Int: PlugValue { }
extension Float: PlugValue { }
extension Double: PlugValue { }
extension SIMD3: PlugValue where Scalar == Int32 { }
struct Plug<Value: PlugValue> {
var value: Value
init(_ value: Value) {
self.value = value
}
}
protocol AnyPlug {
var anyValue: PlugValue { get }
}
extension AnyPlug {
subscript<Value: PlugValue>(type: Value.Type = Value.self) -> Value {
anyValue as? Value ?? .init()
}
func callAsFunction<Value: PlugValue>(_ type: Value.Type = Value.self) -> Value {
anyValue as? Value ?? .init()
}
}
extension Plug: AnyPlug {
var anyValue: PlugValue { value }
}
let plugs: [AnyPlug] = [
Plug(1),
Plug(2.3 as Float),
Plug(4.5),
Plug([6, 7, 8] as SIMD3)
]
plugs[0][Int.self] // 1
plugs[1][Double.self] // 0
plugs[1][] as Float // 2.3
let double: Double = plugs[2]() // 4.5
plugs[3](SIMD3.self).y // 7
With the array of protocols, do you have to down cast them into their Plug when retrieving them every time?
Essentially. This is true of all heterogenous sequences. Here are your options:
extension Array: PlugValue where Element: PlugValue { }
let plug: AnyPlug = Plug([0.1, 1.1, 2.1])
(plug as? Plug<[Double]>)?.value[1]
(plug.anyValue as? [Double])?[1]
extension Plug {
enum Error: Swift.Error {
case typeMismatch
}
}
extension AnyPlug {
func callAsFunction<Value: PlugValue, Return>(_ closure: (Value) -> Return) throws {
guard let value = anyValue as? Value
else { throw Plug<Value>.Error.typeMismatch }
closure(value)
}
}
try plug { (doubles: [Double]) in doubles[1] } // 1.1
try plug { ($0 as [Double])[1] } // 1.1
try plug { $0 as Int } // <Swift.Int>.Error.typeMismatch
I managed to find a solution that worked for my needs.
I am still looking at finding a better way to handle getting the data and their correct type.
import Foundation
import MetalKit
// Creating the PlugType Enum
enum PlugType{
case Integer(Int?)
case Float_(Float?)
case Double_(Double?)
case Vector3(simd_int3)
// default types
static func IntegerType() -> PlugType{ return PlugType.Integer(nil)}
static func FloatType() -> PlugType{ return PlugType.Float_(nil)}
static func DoubleType() -> PlugType{ return PlugType.Double_(nil)}
}
// Implements a way to retrieve the correct value type
extension PlugType{
var IntegerValue: Int{
switch self{
case .Integer(let value):
return value ?? 0
default:
return 0
}
}
var FloatValue: Float{
switch self
{
case .Float_(let value):
return value ?? 0
default:
return 0
}
}
var DoubleValue: Double{
switch self
{
case .Double_(let value):
return value ?? 0
default:
return 0
}
}
}
// Get the string representation of the PlugType
extension PlugType {
var typeName: String{
switch self {
case .Integer: return "Integer"
case .Float_: return "Float"
case .Double_: return "Double"
case .Vector3: return "Vector3"
}
}
var swiftType: Any.Type {
switch self {
case .Integer: return Int.self
case .Float_: return Float.self
case .Double_: return Double.self
case .Vector3: return simd_int3.self
}
}
}
class Plug{
var _value:PlugType?
var type:String? { get{ return _value?.typeName } }
init(_ newValue:PlugType){
_value = newValue
}
func geee<T>(_ input:T) -> T{
switch type {
case "Integer":
return getVal(_value!.IntegerValue) as! T
case "Double":
return getVal(_value!.DoubleValue) as! T
default:
return 0 as! T
}
}
func getVal(_ val:Int) -> Int {
return val
}
func getVal(_ val:Float) -> Float {
return val
}
func getVal(_ val:Double) -> Double {
return val
}
}
var plugs:[Plug] = []
var p1 = Plug(PlugType.Integer(2))

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" }

Swift generics: return type based on parameter type

Say I have a collection of objects inheriting from a common superclass (this is preferable to protocols in this case):
class ObjectSuperClass {
type: ObjectType
}
class ObjectClass1: ObjectSuperClass {
type = .Type1
}
class ObjectClass2: ObjectSuperClass {
type = .Type2
}
I'm looking to create a generic search function like this:
func objectsOfType<T: ObjectSuperClass>(T.class, otherFilter: Any?) -> [T]
Which could be used to search for a given sub-type, returning a more specific array of results:
let result = objectsOfType(ObjectClass2.class, otherFilter: nil) -> [ObjectClass2]
(pseudo-swift)
I feel like this is somewhere generics could help, but cannot see where constraints should be placed. Is it possible?
Well remarkably this works...
func filterType<T>(list: [AnyObject]) -> [T]
{
return list.filter{ $0 is T }.map{ $0 as! T }
}
...provided you assign the result to something that has been explicitly typed, as in the following example:
class ObjectSuperClass: CustomStringConvertible
{
let myType: String
init(aString: String)
{
myType = aString
}
var description: String { return myType }
}
class ObjectClass1: ObjectSuperClass
{
init()
{
super.init(aString: "<t 1>")
}
}
class ObjectClass2: ObjectSuperClass
{
init()
{
super.init(aString: "<t 2>")
}
}
let unfilteredList: [AnyObject] = [ ObjectClass1(), ObjectClass2(), ObjectSuperClass(aString: "<Who knows>")]
let filteredList1: [ObjectClass1] = filterType(list: unfilteredList)
print("\(filteredList1)") // <t 1>
let filteredList2: [ObjectClass2] = filterType(list: unfilteredList)
print("\(filteredList2)") // <t 2>
let filteredList3: [ObjectSuperClass] = filterType(list: unfilteredList)
print("\(filteredList3)") // [<t 1>, <t 2>, <Who knows>]
T is inferred in each case from the requested return type. The function itself filters the original array based on whether the elements are of the required type and then force casts the filtered results to the correct type.
If you want an "extra filter" you don't need to explicitly type the results as long as T can be inferred from your extra filter function.
func extraFilterType<T>(list: [AnyObject], extraFilter: T -> Bool) -> [T]
{
return list.filter{ $0 is T }.map{ $0 as! T }.filter(extraFilter)
}
let filteredList = extraFilterType(unfilteredList){
(element : ObjectClass2) -> Bool in
!element.description.isEmpty
}
print("\(filteredList)") // <t 2>
EDIT
A slicker version of the filterType function would use flatMap()
func filterType<T>(list: [Any]) -> [T]
{
return list.flatMap{ $0 as? T }
}
EDIT 2
Flatmap is deprecated for optionals, since Swift 4.something, use compactMap
func filterType<T>(list: [Any]) -> [T]
{
return list.compactMap{ $0 as? T }
}
This is the closest approximation I can come up with:
func objectsOfType<T: ObjectSuperClass>(type type: T.Type) -> [T] {
// Just returns an array of all objects of given type
}
func objectsOfType<T: ObjectSuperClass>(type type: T.Type, predicate: T -> Bool) -> [T] {
// Uses predicate to filter out objects of given type
}
Usage:
let bar = objectsOfType(type: ObjectClass1.self)
let baz = objectsOfType(type: ObjectClass2.self) {
// Something that returns Bool and uses $0
}
Technically, you can also go without type argument in the above, but then you will need to have explicitly typed receivers (bar and baz in the above example) so that Swift can correctly infer the types for you and use the right version of the generic function.
You can implement the function like this:
func objectsOfType<T: ObjectSuperClass>(objects: [ObjectSuperClass], subclass: T.Type, otherFilter: (T->Bool)?) -> [T] {
if let otherFilter = otherFilter {
return objects.filter{$0 is T && otherFilter($0 as! T)}.map{$0 as! T}
} else {
return objects.filter{$0 is T}.map{$0 as! T}
}
}
Usage example:
objectsOfType(arrayOfObjects, subclass: ObjectClass1.self, otherFilter: nil)
Note that I'm not a fan of forced casting, however in this scenario it should not cause problems.
Or, the more verbose version of the function, with one less forced cast:
func objectsOfType<T: ObjectSuperClass>(objects: [ObjectSuperClass], subclass: T.Type, otherFilter: (T->Bool)?) -> [T] {
return objects.filter({object in
if let object = object as? T {
if let otherFilter = otherFilter {
return otherFilter(object)
} else {
return true
}
} else {
return false
}
}).map({object in
return object as! T
})
}