基于具有关联值的Swift枚举过滤数组 - 不提及关联值

时间:2018-03-22 20:05:08

标签: swift filter enums

对于某些情况,我有一个带有相关值的枚举:

enum Foo {
    case a
    case b
    case c(String?)
}

我还有一个带有此枚举的结构作为变量

struct Bar {
    var foo: Foo
}

然后我有一个这个对象的数组

let array:[Bar] = [Bar(foo: .a), Bar(foo: .c(nil)), Bar(foo: .c("someString"))]

我想基于它收到类似

的情况创建一个对该数组的子集进行操作的函数
func printOnly(objectsWithCase: Foo)

到目前为止它非常简单,但现在这里有一个问题:对于这个操作,我想要忽略相关的值。

我想让这个函数能够在不提及相关值的情况下采用.c个案,就像说“给我.c的那个而不管相关的值”。

我换句话说 - 我想传递像.c(_)这样的东西(当然这不起作用)并让它返回(在这种情况下打印)Bar(foo: .c(nil))和{{1 }}

到目前为止,我只想出更改函数声明来进行过滤关闭而不是像这样的情况:

Bar(foo: .c("someString"))

我想知道是否有办法在Swift中执行此操作,同时传递案例而不是条件块?

3 个答案:

答案 0 :(得分:2)

您可以在模式匹配操作中将下划线用作外卡:

array.filter {
    switch $0.foo {
        case .a: return true // keep a
        case .b: return false // reject b
        case .c(_): return true // keep c, regardless of assoc. value.
    }
}

答案 1 :(得分:1)

//: Playground - noun: a place where people can play
    enum Foo {
        case a
        case b
        case c(String?)
    }
    struct Bar {
        var foo: Foo
    }
    let array:[Bar] = [Bar(foo: .a), Bar(foo: .c(nil)), Bar(foo: .c("someString"))]
    func printArray(array: [Bar], condition: (Bar) -> Bool) {
        let tmp = array.filter(condition)
        print(tmp)
    }
    printArray(array: array) { bar in
        switch bar.foo {
        case .c:
            return true
        default:
            return false
        }
    }

printArray(array: array) { bar in
    if case let Foo.c = bar.foo {
        return true
    }
    return false
}

修改

//: Playground - noun: a place where people can play
enum Foo: Equatable {
    case a
    case b
    case c(String?)
}
func ==(lhs: Foo, rhs: Foo) -> Bool {
    switch (lhs, rhs) {
    case (.a, .a), (.b, .b), (.c, .c):
        return true
    default:
        return false
    }
}
struct Bar {
    var foo: Foo
}

let array:[Bar] = [Bar(foo: .a), Bar(foo: .c(nil)), Bar(foo: .c("someString"))]
func printArray(array: [Bar], condition: (Bar) -> Bool) {
    let tmp = array.filter(condition)
    print(tmp)
}

func printOnly(objectsWithCase wantedCase: Foo) {
    printArray(array: array) { bar in
        if wantedCase == bar.foo {
            return true
        } else {
            return false
        }
    }
}
printOnly(objectsWithCase:.c(nil))

答案 2 :(得分:1)

虽然这在技术上并不是你所要求的(我不认为有任何方法可以通过枚举来实现这一点),你可以编写一个“假”枚举,其中包含一个匹配任何你的通配符c想。这将为您提供完全相同的语法。

1)将Foo替换为以下

struct Foo: Equatable {

    let rawValue: String
    let associatedObject: String?
    let isWildcard: Bool

    fileprivate init(rawValue: String, associatedObject: String?, isWildcard: Bool) {
        self.rawValue = rawValue
        self.associatedObject = associatedObject
        self.isWildcard = isWildcard
    }

    static var a: Foo {
        return Foo(rawValue: "a", associatedObject: nil, isWildcard: false)
    }

    static var b: Foo {
        return Foo(rawValue: "b", associatedObject: nil, isWildcard: false)
    }

    static var c: Foo {
        return Foo(rawValue: "c", associatedObject: nil, isWildcard: true)
    }

    static func c(_ value: String?) -> Foo {
        return Foo(rawValue: "c", associatedObject: value, isWildcard: false)
    }
}

func ==(left: Foo, right: Foo) -> Bool {
    // Match rawValue + associatedObject unless we have a wildcard
    return (left.rawValue == right.rawValue)
      && (left.associatedObject == right.associatedObject || left.isWilcard || right.isWildcard)
}

2)使用printOnly

实施==功能
func printOnly(objects: [Bar], with match: Foo) {
    objects.filter { $0.foo == match }.forEach { print($0) }
}

3)成功

printOnly(objects: array, with: .c) // [.c(nil), .c("someString")]

<强>讨论

除了额外的样板代码之外,此方法的主要缺点是您必须创建不应允许的枚举值。此方法将您的责任仅用作通配符,而不是真正的枚举值。它也不能保证您无法创建其他枚举案例,尽管您应该能够通过制作唯一的初始化程序fileprivate来缓解这种情况。

否则,这会给你完全相同的界面和enum给你的功能,你可以像以前一样定义你的案例

let array = [Bar(foo: .a), Bar(foo: .c(nil)), Bar(foo: .c("Hello")]

最后,您仍然可以在交换机中使用它,除非您始终需要添加default语句。

switch Foo.c("Hello") {
case .a:
    print("A")
case .b:
    print("B")
case .c: // will match .c(nil) and .c("someString")
    print("C")
default:
    break
}