我创建了一个最小的例子,几乎可以在任何执行任何地方粘贴到新项目中,这应该提供更好的细节,然后是任何描述:
func testFlatMap() -> Data? { class MyObject { let a: String let b: Int init(a: String,b: Int) { self.a = a; self.b = b } func dictionary() -> [String: Any] { var dictionary: [String: Any] = [String: Any]() dictionary["a"] = a dictionary["b"] = b return dictionary } } let objects: [MyObject] = [ MyObject(a: "first",b: 1),MyObject(a: "second",b: 2),MyObject(a: "third",b: 3) ] var dictionary: [String: Any] = [String: Any]() dictionary["objects"] = objects.flatMap { $0.dictionary() } dictionary["objects2"] = objects.map { $0.dictionary() } print("Type of first array: " + String(describing: type(of: dictionary["objects"]!))) print("Type of first element: " + String(describing: type(of: (dictionary["objects"] as! [Any]).first!))) print("Type of second array: " + String(describing: type(of: dictionary["objects2"]!))) print("Type of second element: " + String(describing: type(of: (dictionary["objects2"] as! [Any]).first!))) return try? JSONSerialization.data(withJSONObject: dictionary,options: []) } _ = testFlatMap()
所以这段代码崩溃了
‘NSInvalidArgumentException’,reason: ‘Invalid type in JSON write
(_SwiftValue)’
(使用do-catch没有区别,这是第一个WTH,但现在让我们离开)
那么让我们看一下日志所说的内容:
Type of first array: Array<(key: String,value: Any)> Type of first element: (key: String,value: Any) Type of second array: Array<Dictionary<String,Any>> Type of second element: Dictionary<String,Any>
第二个是我们所期望的,但第一个只有那里的元组.这是自然的,有意的吗?
在你全力以赴“为什么使用flatMap作为非可选值”之前,让我解释一下func dictionary() – > [String:Any]曾经是func dictionary() – > [字符串:任何]?因为它跳过了缺少某些数据的项目.
Type of first array: Array<Dictionary<String,Any>> Type of first element: Dictionary<String,Any> Type of second array: Array<Optional<Dictionary<String,Any>>> Type of second element: Optional<Dictionary<String,Any>>
这意味着第一个解决方案是正确的.在变化时,没有任何警告,也没有任何警告.该应用程序将突然开始崩溃.
最后的问题显然是“如何避免这种情况?”没有太多的工作.使用dictionary [“objects”] = objects.flatMap {$0.dictionary()}作为[[String:Any]]似乎可以做到保留单行的技巧.但使用它似乎非常愚蠢.
解决方法
现在转到flatMap:你的问题是提出SE-0187的原因之一. flatMap有3个重载:
Sequence.flatMap<S>(_: (Element) -> S) -> [S.Element] where S : Sequence Optional.flatMap<U>(_: (Wrapped) -> U?) -> U? Sequence.flatMap<U>(_: (Element) -> U?) -> [U]
>当你的dictionary()函数返回一个非可选项时,它会使用第一个重载.由于字典是一系列键值元组,因此您可以获得元组数组.
>当你的dictionary()函数返回一个可选项时,它使用第三个重载,它基本上过滤掉了nils.这种情况可能非常混乱,因此建议(并接受)SE-0187将此重载重命名为compactMap.
从Swift 4.1(Xcode 9.3,目前处于测试阶段)开始,您可以使用compactMap:
// Assuming dictionary() returns [String: Any]? dictionary["objects"] = objects.compactMap { $0.dictionary() }
对于Swift< 4.1,您提供的解决方案是唯一有效的解决方案,因为它为编译器提供了第三个重载的提示:
dictionary["objects"] = objects.flatMap { $0.dictionary() } as [[String: Any]]