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

问题描述 投票:0回答:6
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
的数组。我需要使用什么谓词

arrays swift filter enums
6个回答
26
投票

过滤器函数既可以作为数组上的全局函数调用,也可以作为实例方法调用(我更喜欢后者,因为它更面向对象)。

它接受一个带有一个参数(正在评估的元素)的闭包,该参数返回一个布尔值(指示该元素是否符合所需条件)。

由于这是一个在明确情况下的简单闭包,因此可以使用缩写形式。

我想其他“With”情况会添加到您的枚举中,因此您可以使用类似的内容:

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

这应该可以解决你的示例中的问题。


10
投票

正如有人已经提到的,对于 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") }

2
投票

您可以尝试向枚举添加一个带有计算属性的简单扩展,并过滤该属性:

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

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

enums.filter { $0.isC }

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

这可能可以通过 Swift 2.0 绑定

if
语句中的关联值来简化。


0
投票

受到@JessySwiftLee的启发,这是我的解决方案:

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

0
投票

您可以通过实现

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
© www.soinside.com 2019 - 2024. All rights reserved.