如何在Swift中创建一个 predicate 来过滤具有关联值的枚举数组?

zmeyuzjn  于 2023-01-12  发布在  Swift
关注(0)|答案(6)|浏览(197)
enum EnumType {
    case WithString(String)
}

var enums = [EnumType]()

enums.append(EnumType.WithString("A"))
enums.append(EnumType.WithString("B"))
enums.append(EnumType.WithString("C"))
enums.append(EnumType.WithString("D"))
enums.append(EnumType.WithString("E"))
enums.append(EnumType.WithString("F"))

如何过滤我的enums数组来找到一个关联值等于C的数组。我需要使用什么** predicate **?

ldxq2e6h

ldxq2e6h1#

filter函数既可以作为数组上的全局函数调用,也可以作为示例方法调用(我更喜欢后者,因为它更面向对象)。
它接受一个带有一个参数(被求值的元素)的闭包,该参数返回一个布尔值(指示元素是否匹配所需的条件)。
因为它是一个简单的闭包,在一个明确的情况下,可以使用缩写形式。
我猜其他的“With”情况会被添加到枚举中,所以您可以使用类似于以下的内容:

let filteredArray = enums.filter { 
    switch $0 {
      case let .WithString(value):
        return value == "C"
      default:
        return false
    }
 }

在你的例子中,这应该可以达到目的。

rryofs0p

rryofs0p2#

正如有人已经提到的,对于Swift〉2.0,有if case语句可用:

enums.filter {
  if case .WithString("C") = $0 {
    return true
  }
  return false
}

但是,这看起来并不好,特别是如果你要重复同样的逻辑,我们可以做的是使EnumType符合Equatable:

extension EnumType: Equatable {
}

func ==(lhs: EnumType, rhs: EnumType) -> Bool {
    switch (lhs, rhs) {
    case (.WithString(let lStr), .WithString(let rStr)):
        return lStr == rStr
    }
}

现在您可以:

enums.filter { $0 == .WithString("C") }
ffscu2ro

ffscu2ro3#

你可以尝试添加一个简单的扩展,它带有一个计算属性到你的枚举和过滤器到那个属性:

extension EnumType {
  var isC: Bool {
    switch self {
    case .WithString(let message): return message == "C"
    default: return false
    }
  }
}

在此之后,您可以像往常一样简单地使用过滤:

enums.filter { $0.isC }
flvlnr44

flvlnr444#

var filteredArray = enums.filter { element in
    switch element {
    case EnumType.WithString(let string):
        return string == "A"
    default:
        return false
    }
}

这可能可以通过Swift 2.0绑定if语句中的关联值来简化。

vx6bjr1n

vx6bjr1n5#

受@Jessy和SwiftLee的启发,我的解决方案如下:

// -----------------------
//     CaseReflectable
// -----------------------

// designed for enums only 
// (use it on other types not recommended)
protocol CaseReflectable {}

// default behaviors.
extension CaseReflectable {
    
    /// case name
    var caseName: String {
        let mirror = Mirror(reflecting: self)
        // enum cases:
        // - normal case: no children
        // - case with associated values: one child (with label)
        guard let label = mirror.children.first?.label else {
            return "\(self)"    // normal case
        }
        // case with associated values
        return label
    }
    
    /// associated values
    var associatedValues: Any? {
        
        // if no children, a normal case, no associated values.
        guard let firstChild = Mirror(reflecting: self).children.first else {
            return nil
        }
        
        return firstChild.value
    }
}

// --------------------------
//     custom operator ~=
// --------------------------

/// match enum cases with associated values, while disregarding the values themselves.
/// usage: `Enum.enumCase ~= instance`
func ~= <Enum: CaseReflectable, AssociatedValue>(
    // an enum case (with associated values)
    enumCase: (AssociatedValue) -> Enum,    // enum case as function
    // an instance of Enum
    instance: Enum
) -> Bool 
{
    // if no associated values, `instance` can't be of `enumCase`
    guard let values = instance.associatedValues else { return false }
    // if associated values not of the same type, return false
    guard values is AssociatedValue else { return false }
    // create an instance from `enumCase` (as function)
    let case2 = enumCase(values as! AssociatedValue)
    // if same case name, return true
    return case2.caseName == instance.caseName
}

// ------------
//     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)]
lvjbypge

lvjbypge6#

通过实现Equatable协议,您可以实现更高的可重用性:

enum EnumType {
    case WithString(String)
}

extension EnumType: Equatable {

    static func ==(lhs: EnumType, rhs: String) -> Bool {
        switch lhs {
        case .WithString(let value):
            return value == rhs
        }
    }
}

EnumType.WithString("F") == "A" // false
EnumType.WithString("F") == "F" // true

相关问题