red*_*t84 12 generics dictionary swift swift-extensions swift2
我有这个功能:
func flatten<Key: Hashable, Value>(dict: Dictionary<Key, Optional<Value>>) -> Dictionary<Key, Value> {
var result = [Key: Value]()
for (key, value) in dict {
guard let value = value else { continue }
result[key] = value
}
return result
}
Run Code Online (Sandbox Code Playgroud)
如您所见,它将[Key: Value?]字典转换为[Key: Value]一个字典(没有可选字段).
我想Dictionary用一个新方法扩展该类只适用于值为Optional任何类型的类,但我无法为字典的泛型参数添加约束.
这是我试过的:
extension Dictionary where Value: Optional<Any> {
func flatten() -> [Key: Any] {
var result = [Key: Any]()
for (key, value) in self {
guard let value = value else { continue }
result[key] = value
}
return result
}
}
Run Code Online (Sandbox Code Playgroud)
但失败并出现错误:
Type 'Value' constrained to non-protocol type 'Optional<Any>'
Run Code Online (Sandbox Code Playgroud)
cez*_*eng 16
在Playground中试用此代码:
// make sure only `Optional` conforms to this protocol
protocol OptionalEquivalent {
typealias WrappedValueType
func toOptional() -> WrappedValueType?
}
extension Optional: OptionalEquivalent {
typealias WrappedValueType = Wrapped
// just to cast `Optional<Wrapped>` to `Wrapped?`
func toOptional() -> WrappedValueType? {
return self
}
}
extension Dictionary where Value: OptionalEquivalent {
func flatten() -> Dictionary<Key, Value.WrappedValueType> {
var result = Dictionary<Key, Value.WrappedValueType>()
for (key, value) in self {
guard let value = value.toOptional() else { continue }
result[key] = value
}
return result
}
}
let a: [String: String?] = ["a": "a", "b": nil, "c": "c", "d": nil]
a.flatten() //["a": "a", "c": "c"]
Run Code Online (Sandbox Code Playgroud)
因为您无法在where协议扩展的子句中指定确切类型,所以您可以准确检测Optional类型的一种方法是使OptionalUNIQUELY符合协议(例如OptionalEquivalent).
为了获得的包裹的值类型Optional,我所定义的typealias WrappedValueType在自定义协议OptionalEquivalent,然后由可选的延伸,assgin的Wrapped到WrappedValueType,则可以得到在压平方法的类型.
请注意,该sugarCast方法只是为了启用使用语句Optional<Wrapped>来转换为Wrapped?(这是完全相同的)guard.
UPDATE
感谢Rob Napier的评论,我简化并重命名了sugarCast()方法并重命名了协议,使其更容易理解.
小智 7
你可以用更简单的方式做到这一点.这适用于Swift 4:
extension Dictionary {
func flatten<Wrapped>() -> [Key: Wrapped] where Value == Optional<Wrapped> {
return filter { $1 != nil }.mapValues { $0! }
}
}
Run Code Online (Sandbox Code Playgroud)
如果您不喜欢使用更高阶函数或需要与以前版本的Swift兼容,您也可以这样做:
extension Dictionary {
func flatten<Wrapped>() -> [Key: Wrapped] where Value == Optional<Wrapped> {
var result: [Key: Wrapped] = [:]
for (key, value) in self {
guard let value = value else { continue }
result[key] = value
}
return result
}
}
Run Code Online (Sandbox Code Playgroud)