首页 > 解决方案 > 在 Swift 中减少 Result 数组

问题描述

假设在映射数组时调用了一个函数,并且该函数返回一个Result. 例如:

func parseFeedItem(_ object: Any) -> Result<FeedItem, Error> {...}

func parseFeed(_ root: Any) -> Result<Feed, Error> {
    ...
    let items = objects.compactMap { parseFeedItem($0) }
    ...
}

映射(items上图)的结果将是一个 s 数组Result。理想情况下,在此之后,我们想知道结果的“联合”,即Result

基本上,我想出了:

typealias Results<T, E> = Result<[T], E> where E: Error

let r: Results<FeedItem, Error> = items.reduce(.success([FeedItem]())) { (accumulator, result) -> Results<FeedItem, Error> in
    switch accumulator {
    case .failure(_):
        return accumulator
    case .success(let array):
        switch result {
        case .failure(let error):
            return .failure(error)
        case .success(let value):
            var newArray = array
            newArray.append(value)
            return .success(newArray)
        }
    }
}

例如,如果只使用successes 运行:

let items: [Result<FeedItem, Error>] = [
    .success(FeedItem(1)),
    .success(FeedItem(2)),
    .success(FeedItem(3)),
]
... run `reduce` defined above ...
print(r) // .success([FeedItem(1), FeedItem(2), FeedItem(3)])

如果数组至少包含一个failure,例如:

let items: [Result<FeedItem, Error>] = [
    .success(FeedItem(1)),
    .success(FeedItem(2)),
    .failure(MyError.blah),
]
... run `reduce` defined above ...
print(r) // .failure(MyError.blah)

所以这行得通。但是,这里有一些问题:

标签: swift

解决方案


有一种更短且通用的方法来完成此操作。Result 类型有两个非常有用的方法:map()flatMap(),请参阅链接的 Apple 文档以准确了解它们的工作原理。

使用它们,我构建了这个通用函数,它允许您在包含在 Result 中的类型之间执行自定义操作,如果操作的两个成员都是成功的,或者第一次遇到失败,则给出 .success(和操作的结果)一个错误 :

func resultOperation<T, E: Error>(_ lhs: Result<T, E>, _ rhs: Result<T, E>, _ operation: ((T, T) -> T)) -> Result<T, E> {
    lhs.flatMap { (lhsValue) -> Result<T, E> in
        rhs.map { (rhsValue) -> T in
            return operation(lhsValue, rhsValue)
        }
    }
}

在您的情况下,操作参数将是 + 运算符,它将两个数组的元素添加到一个数组中。代码如下所示:

let r: Results<FeedItem, Error> = items.reduce(.success([FeedItem]())) { resultOperation($0, $1, +) }

推荐阅读