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

如何過濾我的enums數組以找到關聯值等於C的數組。 我需要使用什么謂詞

filter 函數既可以作為數組上的全局函數調用,也可以作為實例方法調用(我更喜歡后者,因為它更面向 OO)。

它接受一個帶有一個參數(被評估的元素)的閉包,該參數返回一個布爾值(指示該元素是否符合要求的條件)。

由於它是一個在明確情況下的簡單閉包,因此可以使用縮寫形式。

我想其他“With”案例會添加到您的枚舉中,因此您可以使用以下內容:

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

在您的示例中,這應該可以解決問題。

正如有人已經提到的,對於 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") }

您可以嘗試向您的枚舉添加一個帶有計算屬性的簡單擴展並過濾到該屬性:

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

在此之后,您可以像往常一樣簡單地使用過濾:

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

這可能可以通過 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"))

如何過濾我的enums數組以找到關聯值等於C那個。 我需要使用哪些謂詞

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

你可以通過實現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