Try this code in the 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"]
Because you cannot specify an exact type in the where
clause of a protocol extension, one way you may detect exactly the Optional
type is to make Optional
UNIQUELY conforms to a protocol (say OptionalEquivalent
).
In order to get the wrapped value type of the Optional
, I defined a typealias WrappedValueType
in the custom protocol OptionalEquivalent
and then made an extension of Optional, assgin the Wrapped
to WrappedValueType
, then you can get the type in the flatten method.
Note that the sugarCast
method is just to cast the Optional<Wrapped>
to Wrapped?
(which is exactly the same thing), to enable the usage guard
statement.
UPDATE
Thanks to Rob Napier 's comment I have simplified & renamed the sugarCast() method and renamed the protocol to make it more understandable.
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…