【问题标题】:Mapping swift enum with associated values将 swift 枚举与关联值映射
【发布时间】:2016-06-09 15:05:02
【问题描述】:

假设我们有一个带有关联值类型的枚举。在下面的示例中,两种值类型是简单的对象,其中包含要共享的图像和 url。

enum Content {
  case Image(ShareableImage)
  case Video(ShareableVideo)
}

现在让我们有一组视频和图像案例。

let media: [Content] = [*a lot of enum cases inside here*]  

到目前为止,上面的所有代码都不能在代码库中以任何方式更改,我需要使用它。


我的问题从这里开始:

让我们用媒体过滤数组以仅显示图像

    let imageOnlyCases: [Content] = media.filter { item -> Bool in

        switch item {
        case .Image: return true
        default: return false
        }
    }

下一步,我想从枚举数组中获取它们关联值的数组

[Content] -> [ShareableImage] by using map.

所以我这样做

    let shareablemages = imageOnlyCases.map { imageCase -> ShareableImage in

        switch imageCase {

        case .Image(let image): return image
        default: return  WHAT TO DO HERE?
        }
    }

你看,我的返回类型有问题..我知道枚举案例都是 .Image..我想要一个简单的地图。但是快速的语法对我没有帮助。

有什么想法吗?

【问题讨论】:

  • 我会返回 nil,所以你有一个可选的关联值数组,之后你可以过滤它以摆脱 nil。

标签: arrays swift enums


【解决方案1】:

您可以在.Image 的情况下返回image,否则在.flatMap 操作中返回nil(以“过滤”出nil 条目):

/* Example */
enum Foo {
    case Bar(Int)
    case Baz(Int)
}

let foo: [Foo] = [.Bar(1), .Bar(9),. Baz(3), .Bar(39), .Baz(5)]

/* 1. using 'switch' */
let barOnlyValues: [Int] = foo.flatMap {
    switch $0 {
    case .Bar(let val): return val
    case _: return nil
    }}

/* 2. alternatively, as pointed out in MartinR:s answer; 
      as you're only looking for a single case, the alternative
      'if case let' clause could be preferred over 'switch':     */
let barOnlyValuesAlt: [Int] = foo.flatMap {
    if case let .Bar(val) = $0 { return val }
    else { return nil }}                               

print(barOnlyValues) // [1, 9, 39]

应用于您的用例:请注意,您无需执行过滤来创建imageOnlyCases 数组,因为您可以将上述内容直接应用于media 数组:

/* 1. using switch */
let shareableImages : [ShareableImage] = media.flatMap {
    switch $0 {
    case .Image(let image): return image
    case _: return nil
    }}

/* 2. 'if case let' alternative, as per MartinR:s suggestion */
let shareableImagesAlt : [ShareableImage] = media.flatMap {
    if case let .Image(image) = $0 { return image }
    else { return nil }}

免责声明:我无法在实践中验证您的具体用例,因为我无权访问 ShareableImage 类/结构。

(感谢@MartinR 建议.map{ ... }.flatMap{ ... } 可以简化为.flatMap{ ... })。

【讨论】:

  • 现在你更快了 :) – 更简单:media.flatMap { ... } 而不是单独的 map() 和 flatMap 调用。
  • 还有人觉得if case let .Image(let image) = value { ... } 看起来很奇怪吗?我只是不明白这种语法。 Swift 书中只有一个例子,那就是展开可选的。
  • @nekonari 语言指南中的示例 (patterns) 确实涵盖了访问可选项的包装值,但这只是针对 enum 案例的模式匹配结合绑定到模式匹配成功时的关联值。在这种情况下,Optional<Wrapped> 就像任何泛型enum 一样,在enum 的一些(或多个)关联值中使用泛型类型。比较 w。 switch 超过 enum 案例。
  • @dfri 哦,我知道你的意思。但是,如果您只想获取 enum 中的关联值,那么这种模式似乎真的很尴尬。
  • @nekonari 我相信他们选择为if case let ... 保留与使用switch 语句为不同情况绑定关联值时相同的模式。我同意前者的外观需要一些时间来适应,而对于后者(切换),模式匹配感觉很自然。最后,这里的一致性选择(可能有一些异国情调的“成本”),imo,是一个很好的选择。
【解决方案2】:

如果保证只有.Image的情况会发生,那么 在所有其他情况下,您可以致电fatalError()

let shareableImages = imageOnlyCases.map { imageCase -> ShareableImage in

    if case let .Image(image) = imageCase {
        return image
    } else {
        fatalError("Unexpected content")
    }
}

fatalError() 导致程序立即终止。它是 仅适用于“不可能发生”的情况,即寻找编程 错误。

它满足编译器,因为函数被标记为@noreturn

如果您无法保证,请按照建议使用flatMap() 在另一个答案中。

另请注意,您可以在此处使用 if case 与模式代替 switch/case.

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-04-24
    • 2021-11-01
    • 1970-01-01
    相关资源
    最近更新 更多