是否可以在Swift集合上中止map函数?

时间:2017-12-09 01:22:44

标签: swift map-function codable

我们有一个案例,我们需要将Array<Any>类型的对象转交给Array<Codable>。如果原始数组中的任何项目不遵守Codable,那么我们希望整个项目中止并返回nil。

或者当前的方法是手动循环所有内容,沿途进行测试,就像这样......

func makeCodable(sourceArray:Array<Any>) -> Array<Codable>?{

    var codableArray = Array<Codable>()

    for item in sourceArray{

        guard let codableItem = item as? Codable else {
            return nil
        }

        codableArray.append(codableItem)
    }

    return codableArray
}

但是,我想知道使用map命令是否有更简单的方法来执行此操作,但是如果任何元素可以使用它,则需要它进行短路。被映射。这是我不确定或不可能的事情。

例如,这个伪代码......

func makeCodable(sourceArray:Array<Any>) -> Array<Codable>?{

    return sourceArray.map({ $0 as? Codable});
}

这是可能的,还是我们原来的方式是正确/唯一的方式?

2 个答案:

答案 0 :(得分:1)

以下是使用mapthrows的解决方案。

func makeCodable(sourceArray: [Any]) -> [Codable]? {
    enum CodableError: Error {
        case notCodable
    }

    let res: [Codable]? = try? sourceArray.map {
        guard let codable = $0 as? Codable else {
            throw CodableError.notCodable
        }

        return codable
    }

    return res
}

let res = makeCodable2(sourceArray: [5, 6.5, "Hi", UIView()])
print(res) // nil

这是一个让makeCodable抛出并返回一个非可选数组的变体:

enum CodableError: Error {
    case notCodable
}

func makeCodable(sourceArray: [Any]) throws -> [Codable] {
    let res: [Codable] = try sourceArray.map {
        guard let cod = $0 as? Codable else {
            throw CodableError.notCodable
        }

        return cod
    }

    return res
}

do {
    let res = try makeCodable(sourceArray: [5, 6.5, "Hi"])
    print(res) // prints array
    let bad = try makeCodable(sourceArray: [5, 6.5, "Hi", UIView()])
    print(bad)
} catch {
    print(error) // goes here on 2nd call
}

答案 1 :(得分:0)

作为@rmaddy shows,您可以利用map(_:)可以接受抛出闭包的事实,并在抛出错误时停止映射,将错误传播回调用者(然后您可以吸收try?)。

对此的一个细微变化是定义您自己的thr cast(_:to:)函数以在转换闭包中调用:

struct TypeMismatchError : Error {
  var expected: Any.Type
  var actual: Any.Type
}

func cast<T, U>(_ x: T, to _: U.Type) throws -> U {
  guard let casted = x as? U else {
    throw TypeMismatchError(expected: U.self, actual: type(of: x))
  }
  return casted
}

func makeCodable(sourceArray: [Any]) -> [Codable]? {
  return try? sourceArray.map { try cast($0, to: Codable.self) }
}

虽然我们完全忽略了在这种情况下抛出的错误,但我发现它在其他情况下偶尔会有一个投掷强制转换函数(你当然也可以通过使makeCodable抛出来传播错误功能并使用try)。

然而,尽管如此,请注意您的结果[Codable]?在当前形式中确实不太有用。您无法解码其中的内容,因为您没有任何具体类型可供处理,并且您无法直接将其编码为protocols don't conform to themselves(即Codable不符合Encodable },所以你不能只是将Codable[Codable]交给JSONEncoder)。

如果您确实想要使用[Codable]进行某些编码,则需要将每个元素包装在符合Encodable的包装器中,例如:

struct AnyEncodable : Encodable {

  var base: Encodable

  init(_ base: Encodable) {
    self.base = base
  }

  func encode(to encoder: Encoder) throws {
    try base.encode(to: encoder)
  }
}

func makeEncodable(sourceArray: [Any]) -> [AnyEncodable]? {
  return try? sourceArray.map {
    AnyEncodable(try cast($0, to: Encodable.self))
  }
}

现在[AnyEncodable]可以传递给JSONEncoder