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

基于具有关联值的Swift枚举筛选数组-不提及关联值,swift,filter,enums,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 p

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

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))
Bar(foo:.c(“someString”)

到目前为止,我只想到了更改函数声明以接受过滤闭包,而不是像这样的情况:

func printArray(array: [Bar], condition: (Bar) -> Bool) {
    let tmp = array.filter(condition)
    print(tmp)
}

我想知道是否有办法在Swift中这样做,同时传递案例而不是条件块?

您可以在模式匹配操作中使用下划线作为通配符:

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.
    }
}

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

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.
    }
}

编辑

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

编辑

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

虽然这在技术上不是您所要求的(我认为没有任何方法可以通过枚举实现),但您可以编写一个包含通配符
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)使用
=

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
来缓解这种情况

否则,这将为您提供与枚举完全相同的接口和功能,您可以像以前一样定义案例

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
}

虽然这在技术上不是您所要求的(我认为没有任何方法可以通过枚举实现),但您可以编写一个包含通配符
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)使用
=

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
来缓解这种情况

否则,这将为您提供与枚举完全相同的接口和功能,您可以像以前一样定义案例

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
}

您不需要通配符模式,
case.c:
也可以这样做。@MartinR Nice,TIL。我可能不清楚这个问题,但我很清楚这些选项。正如我在问题的最后一行中所说,我对维护这个函数声明感兴趣:
func printOnly(objectsWithCase:Foo)
。问题是-你怎么称呼这个函数?不是你如何实现的it@Noam你不能
Foo.c
不是一个有效的值,您可以为
c
进行模式匹配的唯一方法是以可以存储在变量中的方式捕获(模式匹配)行为。。。这就是闭包。你不需要通配符模式,
case.c:
也可以这样做。@MartinR Nice,TIL。我可能不清楚这个问题,但我很清楚这些选项。正如我在问题的最后一行中所说,我对维护这个函数声明感兴趣:
func printOnly(objectsWithCase:Foo)
。问题是-你怎么称呼这个函数?不是你如何实现的it@Noam你不能
Foo.c
不是一个有效的值,您可以为
c
进行模式匹配的唯一方法是以可以存储在变量中的方式捕获(模式匹配)行为。。。这就是结束。是的,这是一个很好的选择,但这不是我想要的。。。我希望维护此函数声明:
func printOnly(objectsWithCase:Foo)
。问题是-你怎么称呼这个函数?是的,这是一个很好的选择,但它不是我想要的。。。我希望维护此函数声明:
func printOnly(objectsWithCase:Foo)
。问题是-如何调用此函数?