使用带有字典的flatMap会产生元组吗?

时间:2018-03-27 13:46:31

标签: ios swift swift4 flatmap

我有一个非常基本和直接的对象映射到字典。我在顶层使用和解析字典。其中一个字段是一系列其他字典。要设置它们,我使用flatMap这似乎是一个合适的方法,但由于里面的对象不可为空,这个方法突然返回元组(至少看起来像这样)而不是字典。

我创建了一个最小的例子,几乎可以在任何执行任何地方粘贴到新项目中,这应该提供比任何描述更好的细节:

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',原因:'JSON写入中的类型无效   (_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() -> [String: Any]?,因为它跳过了缺少某些数据的项目。

因此,只在该方法的末尾添加少量?会将输出更改为:

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() } as [[String: Any]]似乎可以帮助保留单行。但使用它似乎非常愚蠢。

1 个答案:

答案 0 :(得分:3)

我发现您的问题归结为mapflatMapmap是更加一致的,它在预期的工作方式如此,所以我不会深入研究它。

现在转到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()函数返回一个可选项时,它会使用第三个重载,它实际上会过滤掉nil。情况可能非常混乱,因此建议(并接受)SE-0187将此重载重命名为compactMap

从Swift 4.1开始(Xcode 9.3,目前处于测试阶段),您可以使用compactMap

// Assuming dictionary() returns [String: Any]?
dictionary["objects"] = objects.compactMap { $0.dictionary() }

对于Swift&lt; 4.1 ,您提供的解决方案是唯一有效的解决方案,因为它为编译器提供了第三次重载的提示:

dictionary["objects"] = objects.flatMap { $0.dictionary() } as [[String: Any]]