将 flatMap 与字典一起使用会产生元组吗?
Using flatMap with dictionaries produces tuples?
我有一个非常基本和直接的对象到字典的映射。我在顶层使用和解析字典。它的一个字段是一系列其他词典。要设置它们,我使用 flatMap
这似乎是一种合适的方法,但由于内部对象不可为空,因此此方法突然 returns 元组(至少看起来是这样)而不是字典。
我创建了一个最小的示例,可以将其粘贴到新项目中几乎任何您执行的任何地方,它应该提供比任何描述更详细的信息:
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 { [=10=].dictionary() }
dictionary["objects2"] = objects.map { [=10=].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>
第二个是我们所期望的,但第一个只有元组。这是自然的、故意的吗?
在你全力以赴之前 "why use flatMap for non-optional values" 让我解释一下 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>>
这意味着第一个解决方案是正确的。在更改时没有警告,什么也没有。该应用程序将突然开始崩溃。
最后的问题很明显"How to avoid this?"没有太多的工作。使用 dictionary["objects"] = objects.flatMap { [=17=].dictionary() } as [[String: Any]]
似乎可以解决保留单行的问题。但是用这个好像很傻。
我看到您的问题归结为 map
与 flatMap
。 map
是比较一致的,它在这里按预期工作,所以我不会深入研究它。
现在开始 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()
函数 returns a non-optional 时,它使用第一个重载。由于字典是 key-value 元组的序列,因此您得到的是元组数组。
当您的 dictionary()
函数 returns 是可选的时,它使用第三个重载,它基本上过滤掉 nil
。这种情况可能会非常混乱,因此建议(并接受)SE-0187 将此重载重命名为 compactMap
.
从Swift 4.1(Xcode 9.3,目前处于测试阶段)开始,您可以使用compactMap
:
// Assuming dictionary() returns [String: Any]?
dictionary["objects"] = objects.compactMap { [=11=].dictionary() }
对于 Swift < 4.1,您提供的解决方案是唯一有效的解决方案,因为它为编译器提供了进行第三次重载的提示:
dictionary["objects"] = objects.flatMap { [=12=].dictionary() } as [[String: Any]]
我有一个非常基本和直接的对象到字典的映射。我在顶层使用和解析字典。它的一个字段是一系列其他词典。要设置它们,我使用 flatMap
这似乎是一种合适的方法,但由于内部对象不可为空,因此此方法突然 returns 元组(至少看起来是这样)而不是字典。
我创建了一个最小的示例,可以将其粘贴到新项目中几乎任何您执行的任何地方,它应该提供比任何描述更详细的信息:
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 { [=10=].dictionary() }
dictionary["objects2"] = objects.map { [=10=].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>
第二个是我们所期望的,但第一个只有元组。这是自然的、故意的吗?
在你全力以赴之前 "why use flatMap for non-optional values" 让我解释一下 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>>
这意味着第一个解决方案是正确的。在更改时没有警告,什么也没有。该应用程序将突然开始崩溃。
最后的问题很明显"How to avoid this?"没有太多的工作。使用 dictionary["objects"] = objects.flatMap { [=17=].dictionary() } as [[String: Any]]
似乎可以解决保留单行的问题。但是用这个好像很傻。
我看到您的问题归结为 map
与 flatMap
。 map
是比较一致的,它在这里按预期工作,所以我不会深入研究它。
现在开始 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()
函数 returns a non-optional 时,它使用第一个重载。由于字典是 key-value 元组的序列,因此您得到的是元组数组。当您的
dictionary()
函数 returns 是可选的时,它使用第三个重载,它基本上过滤掉nil
。这种情况可能会非常混乱,因此建议(并接受)SE-0187 将此重载重命名为compactMap
.
从Swift 4.1(Xcode 9.3,目前处于测试阶段)开始,您可以使用compactMap
:
// Assuming dictionary() returns [String: Any]?
dictionary["objects"] = objects.compactMap { [=11=].dictionary() }
对于 Swift < 4.1,您提供的解决方案是唯一有效的解决方案,因为它为编译器提供了进行第三次重载的提示:
dictionary["objects"] = objects.flatMap { [=12=].dictionary() } as [[String: Any]]