挑战:筛选关联值枚举数组

作者:Erica Sadun,原文链接,原文日期:2017-01-31
译者:星夜暮晨;校对:Crystal Sun;定稿:CMB

本挑战由 Mike Ash 提供,如下所示,假设有这样一个枚举:

enum Enum {
    case foo(Int)
    case bar(String)
    case qux(Int)
}

这些枚举还组成了的一个数组:

let items: [Enum] = [.foo(1),.bar("hi"),.foo(2)]

需要对数组进行筛选(filter),挑选并创建只包含某种枚举值(case)的新数组。麻烦的是,Swift没有一种类似 ==_= 的操作符,可以让我们忽略枚举的关联值:

// 不起作用
let filtered = items.filter({ $0 == .foo })

那么应该怎么办呢?

第一次尝试

这是第一次尝试。代码尽管很丑,但是能用:

let filtered = items.filter({ 
    switch $0 { case .foo: return true; default: return false } })

Evan Dekhayser 更偏好于 if-case

let filtered = items.filter({ 
    if case .foo = $0 { return true }; return false })

当然,这里也可以使用 guard

let filteredy = items.filter({ 
    guard case .foo = $0 else { return false }; return true })

第二次尝试

尽管同样丑陋,由于节省了几个字符,这段代码看起来要稍微简短一些。不过代码所需的执行步骤远比第一次尝试所的执行步骤要多:

let filtered = items.filter({ 
    for case .foo in [$0] { return true }; return false })

还是很糟糕。

第三次尝试

我个人非常厌恶这个方法,因为我必须要分别为每个枚举值实现一个对应的属性。这简直就是车祸现场:

extension Enum {
    var isFoo: Bool {
        switch self { case .foo: return true; default: return false }
    }
}
let filtered = items.filter({ $0.isFoo })

第四次尝试

这种做法比较恶心,因为它需要占位符来填充 rhs 的值,即便这个值从来没被用过。哦对了,这里就无法传递下划线 (_) 了:

extension Enum {
    static func ~= (lhs: Enum,rhs: Enum) -> Bool {
        let lhsCase = Array(Mirror(reflecting: lhs).children)
        let rhsCase = Array(Mirror(reflecting: rhs).children)
        return lhsCase[0].0 == rhsCase[0].0
    }
}
let filtered = items.filter({ $0 ~= .foo(0) })

第五次尝试

接着我突然灵光一现,我们还可以使用反射 (reflection) 呀!如果不给关联值枚举提供特定值,它就会返回一个 (T) -> Enum函数。我写了很多很多,直到我突然意识到枚举的 名称 并没有保留在其反射当中:

import Foundation

extension Enum {
    var caseName: String {
        return "\(Array(Mirror(reflecting: self).children)[0].0!)"
    }
    
    static func ~= <T>(lhs: Enum,rhs: (T) -> Enum) -> Bool {
        let lhsCase = lhs.caseName
        let prefixString = "Mirror for (\(T.self)) -> "
        let typeOffset = prefixString.characters.count
        let typestring = "\(Mirror(reflecting: rhs).description)"
        let rhsCase = typestring.substring(from: typestring.index(typestring.startIndex,offsetBy: typeOffset))
        return true
    }
}

没错……非常难看,此外,还毫无作用。

征集解决方

我没有深入钻研这个问题,我决定把这个问题作为一个公开的挑战。您是否能想出一个简洁、易读、不怎么可怕(或许我应该说「更优雅」,但拜托,让我任性一下)的方法来实现这个功能呢?我相当怀疑我的第一次尝试可能是最好的,如果这是真的,我会非常难过。

本文由 SwiftGG 翻译组翻译,已经获得作者翻译授权,最新文章请访问 http://swift.gg

相关文章

软件简介:蓝湖辅助工具,减少移动端开发中控件属性的复制和粘...
现实生活中,我们听到的声音都是时间连续的,我们称为这种信...
前言最近在B站上看到一个漂亮的仙女姐姐跳舞视频,循环看了亿...
【Android App】实战项目之仿抖音的短视频分享App(附源码和...
前言这一篇博客应该是我花时间最多的一次了,从2022年1月底至...
因为我既对接过session、cookie,也对接过JWT,今年因为工作...