是否可以中止 Swift 集合上的映射函数?
Is it possible to abort a map function on a Swift collection?
我们有一个案例,我们收到了一个 Array<Any>
类型的对象,我们需要将其转换为 Array<Codable>
。如果原始数组中的任何项目不遵守 Codable
,那么我们希望整个事情中止并且 return 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({ [=12=] as? Codable});
}
这可能吗,还是我们原来的方式是correct/only方式?
这是使用 map
和 throws
的一种解决方案。
func makeCodable(sourceArray: [Any]) -> [Codable]? {
enum CodableError: Error {
case notCodable
}
let res: [Codable]? = try? sourceArray.map {
guard let codable = [=10=] as? Codable else {
throw CodableError.notCodable
}
return codable
}
return res
}
let res = makeCodable2(sourceArray: [5, 6.5, "Hi", UIView()])
print(res) // nil
这是一个变体,它使 makeCodable
抛出并且 return 成为一个非可选数组:
enum CodableError: Error {
case notCodable
}
func makeCodable(sourceArray: [Any]) throws -> [Codable] {
let res: [Codable] = try sourceArray.map {
guard let cod = [=11=] 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
}
与 一样,您可以利用 map(_:)
可以接受抛出闭包这一事实,并将在抛出错误时停止映射,将抛出的错误传播回调用者(您然后可以吸收 try?
).
一个细微的变化是定义你自己的 throwing 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([=10=], to: Codable.self) }
}
虽然我们完全忽略了在这种情况下抛出的错误,但我发现在其他情况下偶尔使用抛出转换函数是有用的(当然你也可以通过使 makeCodable
一个抛出函数并使用 try
).
然而,综上所述,请注意,您得到的 [Codable]?
在当前形式下确实不太有用。你不能从它解码东西,因为你手头没有任何具体类型,你不能直接将它编码为 (即 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([=11=], to: Encodable.self))
}
}
现在 [AnyEncodable]
是您可以传递给的东西,例如 JSONEncoder
。
我们有一个案例,我们收到了一个 Array<Any>
类型的对象,我们需要将其转换为 Array<Codable>
。如果原始数组中的任何项目不遵守 Codable
,那么我们希望整个事情中止并且 return 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({ [=12=] as? Codable});
}
这可能吗,还是我们原来的方式是correct/only方式?
这是使用 map
和 throws
的一种解决方案。
func makeCodable(sourceArray: [Any]) -> [Codable]? {
enum CodableError: Error {
case notCodable
}
let res: [Codable]? = try? sourceArray.map {
guard let codable = [=10=] as? Codable else {
throw CodableError.notCodable
}
return codable
}
return res
}
let res = makeCodable2(sourceArray: [5, 6.5, "Hi", UIView()])
print(res) // nil
这是一个变体,它使 makeCodable
抛出并且 return 成为一个非可选数组:
enum CodableError: Error {
case notCodable
}
func makeCodable(sourceArray: [Any]) throws -> [Codable] {
let res: [Codable] = try sourceArray.map {
guard let cod = [=11=] 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
}
与 map(_:)
可以接受抛出闭包这一事实,并将在抛出错误时停止映射,将抛出的错误传播回调用者(您然后可以吸收 try?
).
一个细微的变化是定义你自己的 throwing 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([=10=], to: Codable.self) }
}
虽然我们完全忽略了在这种情况下抛出的错误,但我发现在其他情况下偶尔使用抛出转换函数是有用的(当然你也可以通过使 makeCodable
一个抛出函数并使用 try
).
然而,综上所述,请注意,您得到的 [Codable]?
在当前形式下确实不太有用。你不能从它解码东西,因为你手头没有任何具体类型,你不能直接将它编码为 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([=11=], to: Encodable.self))
}
}
现在 [AnyEncodable]
是您可以传递给的东西,例如 JSONEncoder
。