filter enum cases
๐พ ็จๅผ๏ผpaiza.io
โฌ๏ธ ้่ฆ๏ผCaseReflectable โญ๏ธ
// ------------
// Enum
// ------------
// enum with associated values
// (conforms to `CaseReflectable`) โญ๏ธ
enum Enum: CaseReflectable {
case int(Int)
case int2(Int)
case person(name: String, age: Int)
case str(String)
}
// ------------
// main
// ------------
let a: Enum = .int(3)
Enum.int ~= a // true
Enum.int2 ~= a // false
let joe = Enum.person(name: "joe", age: 8)
Enum.person ~= joe // true
Enum.int ~= joe // false
// array of enum cases
let items: [Enum] = [
.int(1), .str("hi"), .int(2)
]
// filter enum cases โญ๏ธ
let filtered = items.filter { Enum.int ~= $0 }
print(filtered) // [Enum.int(1), Enum.int(2)]
CaseReflectable - required protocol.
ๅ๏ผๅฐๅบ่ฆๅฆไฝๅฏซๆ่ฝไฝฟ็จไธๅ่ชๆณโ
// โญ custom pattern matching
.enumCase ~= anEnumInstance // with associated value
I needed to know the name of the enum case without any associated types (values?). It turned out that the Mirror API was what I needed.
๐ SwiftLee
๐พ ็จๅผ๏ผpaiza.io
// enum with associated values
enum Enum {
case int(Int)
case str(String)
}
// โญ๏ธ enum case as function
print([3].map(Enum.int)) // [ .int(3) ]
print(type(of: Enum.int)) // (Int) -> Enum โญ๏ธ
// --------------------------------------------
// https://stackoverflow.com/a/67204360/5409815
// --------------------------------------------
/// โญ๏ธ generic pattern matching operator (~=)
/// match enum cases with associated values, while disregarding the values themselves.
/// usage: `Enum.enumCase ~= instance`
public func ~= <Enum, AssociatedValue>(
// an enum case (which accepts associated values of type `AssociatedValue`)
enumCase: (AssociatedValue) -> Enum,
// an instance of Enum
instance: Enum
) -> Bool
{
// if `instance` is of case `enumCase`, return true.
Mirror.associatedValue(of: instance, ifCase: enumCase) != nil
}
// extension
public extension Mirror {
/// get an `enumCase`'s `associatedValue`.
static func associatedValue<AssociatedValue>(
of subject: Any,
_ : AssociatedValue.Type = AssociatedValue.self
) -> AssociatedValue?
{
guard let childValue = Self(reflecting: subject).children.first?.value
else { return nil }
if let associatedValue = childValue as? AssociatedValue {
return associatedValue
}
let labeledAssociatedValue = Self(reflecting: childValue).children.first
return labeledAssociatedValue?.value as? AssociatedValue
}
/// Get an `enum` case's `associatedValue`.
/// - Parameter case: Looks like `Enum.case`.
static func associatedValue<Enum, AssociatedValue>(
of instance: Enum,
ifCase case: (AssociatedValue) throws -> Enum
) rethrows -> AssociatedValue?
{
try associatedValue(of: instance).filter {
.equate(try `case`($0), to: instance) {
Self(reflecting: $0).children.first?.label
}
}
}
}
public extension Optional {
/// Transform `.some` into `.none`, if a condition fails.
/// - Parameters:
/// - isSome: The condition that will result in `nil`, when evaluated to `false`.
func filter(_ isSome: (Wrapped) throws -> Bool) rethrows -> Self {
try flatMap { try isSome($0) ? $0 : nil }
}
}
public extension Equatable {
/// Equate two values using a closure.
static func equate<Wrapped, Equatable: Swift.Equatable>(
_ optional0: Wrapped?, to optional1: Wrapped?,
using transform: (Wrapped) throws -> Equatable
) rethrows -> Bool {
try optional0.map(transform) == optional1.map(transform)
}
}
let a: Enum = .int(3)
print(Enum.int ~= a) // true โญ๏ธ
History
ๅ้ๅง็่งฃๆณ๏ผไฝฟ็จ switch case, if case.
๐พ ็จๅผ๏ผpaiza.io
// โญ๏ธ enum with associated values
enum Enum {
case int(Int)
case str(String)
case boo(Bool)
}
// array of enum cases
let items: [Enum] = [
.int(1), .str("hi"), .int(2)
]
// -----------------
// filtering
// -----------------
// โญ๏ธ `switch-case` pattern
let ints = items.filter { // [ .int(1), .int(2) ]
switch $0 {
case .int: return true
default : return false
}
}
// โญ๏ธ `if-case` pattern
let strs = items.filter { // [ .str("hi') ]
if case .str = $0 { return true }
return false
}
// -------------------
// โญ๏ธ extension
// -------------------
extension Enum {
// โญ๏ธ convenience computed property
var isInt: Bool {
if case .int = self { return true }
return false
}
}
// โญ๏ธ filter using keypath
items.filter(\.isInt) // [.int(1), .int(2)]
Last updated