【问题标题】:Swift - find object in array of enums with associated valuesSwift - 在具有关联值的枚举数组中查找对象
【发布时间】:2020-12-10 07:36:27
【问题描述】:

我有这个枚举:

enum Animal {       
    case cat(CatModel)
    case dog(DogModel)
}

还有一群动物:

var animals: [Animal]

我需要通过 Dog 没有的属性在这个数组中找到一个 Cat 对象。以litterBoxId 为例。

let cat = animals.first(where: {$0.litterBoxId == 7})

这当然有错误:

Value of type 'MyViewController.Animal' has no member 'litterBoxId'

我怎样才能做到这一点?我也试过了

($0 as CatModel).litterBoxId

【问题讨论】:

    标签: arrays swift search enums associated-value


    【解决方案1】:

    您可以通过两种方式使用模式匹配来完成此操作。

    使用开关:

    let cat = animals.first(where: {
        switch $0 {
        case .cat(let catModel) where catModel.litterBoxId == 7:
            return true
        default:
            return false
        }
    })
    

    如果:

    let cat = animals.first(where: {
        if case .cat(let catModel) = $0, catModel.litterBoxId == 7 {
            return true
        }
        return false
    })
    

    更新:正如@Alexander-ReinstateMonica 在他的 commnet 中提到的,将这个逻辑隐藏在这样的函数后面会更合适:

    extension Animal {
        func matches(litterboxID: Int) -> Bool {
            switch self {
            case .cat(let catModel) where catModel.litterBoxId == 7:
                return true
            default:
                return false
            }
        }
    }
    

    然后你的代码会更干净:

    let cat = animals.first(where: { $0.matches(litterboxID: 7) })
    

    【讨论】:

    • 我不建议这样做,这完全是一团糟,掩盖了意图。我建议至少将这些提取到枚举的函数中。
    • @Alexander-ReinstateMonica 那么,枚举中的函数将枚举数组和litterBoxId 作为参数?我没听错吗?
    • 在最表面的层面上,你可以引入func litterboxID(of: Animal, matches: Int) -> Bool,并将你的模式匹配逻辑放在那里,这样你就可以调用animals.first(where: { litterboxID(of: Animal, matches: 7) }。更好的是,您应该在枚举本身中访问这些字段,正如我在回答中提到的(以及它的 cmets)。更好的是,使用可以包含这些字段(如果适用)并允许您轻松转换以获得其余部分的协议
    • @Alexander-ReinstateMonica 谢谢。我用你的一个建议更新了我的答案。
    【解决方案2】:

    这可能不是很好地使用枚举,但这是它可以工作的方式:

    struct CatModel {
        let litterBoxID: Int
    }
    
    struct DogModel {
        let litterBoxID: Int
    }
    
    enum Animal {       
        case cat(CatModel)
        case dog(DogModel)
        
        var litterBoxId: Int {
            switch self {
            case .cat(let cat): return cat.litterBoxID
            case .dog(let dog): return dog.litterBoxID
            }
        }
    }
    
    var animals: [Animal] = []
    let cat = animals.first(where: { $0.litterBoxId == 7 })
    

    使用协议会更好:

    struct CatModel: Animal {
        let litterBoxID: Int
    }
    
    struct DogModel: Animal {
        let litterBoxID: Int
    }
    
    protocol Animal {
        var litterBoxID: Int { get }
    }
    
    var animals: [Animal] = []
    let cat = animals.first(where: { $0.litterBoxID == 7 })
    

    【讨论】:

    • 虽然规范的一部分是 DogModel 没有 litterBoxID。
    • @soleil 然后您可以将litterBoxId 字段设为可选,并写入{ $0.litterBoxID? == 7 }。或者,你可以写var catModel: Cat?var dogModel: Dog?,写{ $0.cat?.litterBoxID == 7 }。在任何一种情况下,更自然的方法是使用协议(包含碰巧共有的任何属性),并针对特定的部分进行转换:{ ($0 as? Cat)?.litterBoxID == 7 }
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-05-08
    相关资源
    最近更新 更多