简体   繁体   English

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

[英]How to create a predicate to filter array of enums with associated values in Swift?

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

How to filter my enums array to find the one with associated value equal C .如何过滤我的enums数组以找到关联值等于C的数组。 What predicate do I need to use?我需要使用什么谓词

The filter function can either be invoked as a global function over an array, or as an instance method (I prefer the later as it is more OO). filter 函数既可以作为数组上的全局函数调用,也可以作为实例方法调用(我更喜欢后者,因为它更面向 OO)。

It accepts a closure with one parameter (the element being evaluated) that return a boolean (indicating whether the element matches the required condition).它接受一个带有一个参数(被评估的元素)的闭包,该参数返回一个布尔值(指示该元素是否符合要求的条件)。

Since it's a simple closure in a clear situation it's ok to use the abbreviated form.由于它是一个在明确情况下的简单闭包,因此可以使用缩写形式。

I guess that other "With" cases would be added to your enum, so you could go with something like:我想其他“With”案例会添加到您的枚举中,因此您可以使用以下内容:

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

That should do the trick in your example.在您的示例中,这应该可以解决问题。

As someone already mentioned, for Swift > 2.0 there's if case statement available:正如有人已经提到的,对于 Swift > 2.0 有if case语句可用:

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

But, it doesn't look nice, especially if you are going to repeat same logic again.但是,它看起来不太好,特别是如果您要再次重复相同的逻辑。 What we can do here is to make EnumType conform to Equatable:我们在这里可以做的是让EnumType符合 Equatable:

extension EnumType: Equatable {
}

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

And now you can just:现在你可以:

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

You could try adding a simple extension with a computed property to your enum and filter to that property:您可以尝试向您的枚举添加一个带有计算属性的简单扩展并过滤到该属性:

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

After this, you could simpy use the filtering as usual:在此之后,您可以像往常一样简单地使用过滤:

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

This can probably be simplified with Swift 2.0 binding of assosciated values in if statements.这可能可以通过 Swift 2.0 在if语句中绑定关联值来简化。

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

How to filter my enums array to find the one with associated value equal C .如何过滤我的enums数组以找到关联值等于C那个。 What predicate do I need to use?我需要使用哪些谓词

Inspired by @Jessy and SwiftLee , here is my solution:@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)]

You can achieve something more reusable by implementing Equatable protocol:你可以通过实现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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM