Given a struct A
, I want to populate a NSDictionary
with values from that struct, provided they are not nil.
To do that I insert all the valu
As @vacawama says, you should generally steer clear of Dictionary
types with optional values, as the Dictionary
API is built around using nil
to indicate the lack of a value for a given key. Therefore using an optional type for a Dictionary
's Value
results in confusing double-wrapped optionals, which can often exhibit unintuitive behaviour.
Another (more overkill) solution would be to directly utilise dictionary literals by defining your own 'value unwrapping' ExpressibleByDictionaryLiteral
type.
extension Dictionary {
/// A dictionary wrapper that unwraps optional values in the dictionary literal
/// that it's created with.
struct ValueUnwrappingLiteral : ExpressibleByDictionaryLiteral {
typealias WrappedValue = Dictionary.Value
fileprivate let base: [Key : WrappedValue]
init(dictionaryLiteral elements: (Key, WrappedValue?)...) {
var base = [Key : WrappedValue]()
// iterate through the literal's elements, unwrapping the values,
// and updating the base dictionary with them.
for case let (key, value?) in elements {
if base.updateValue(value, forKey: key) != nil {
// duplicate keys are not permitted.
fatalError("Dictionary literal may not contain duplicate keys")
}
}
self.base = base
}
}
init(unwrappingValues literal: ValueUnwrappingLiteral) {
self = literal.base // simply get the base of the unwrapping literal.
}
}
You can then simply pass instances of this type to our init(unwrappingValues:)
initialiser:
struct A {
var first: String?
var second: String?
var third: String?
}
let a = A(first: "foo", second: nil, third: "baz")
let dictionary = Dictionary(unwrappingValues: [
"first" : a.first,
"second" : a.second,
"third" : a.third
])
print(dictionary) // ["third": "baz", "first": "foo"]
It's usually not a good idea to have a dictionary with a value that is optional. Dictionaries use the assignment of nil
as an indication that you want to delete a key/value pair from the dictionary. Also, dictionary lookups return an optional value, so if your value is optional you will end up with a double optional that needs to be unwrapped twice.
You can use the fact that assigning nil
deletes a dictionary entry to build up a [String : String]
dictionary by just assigning the values. The ones that are nil
will not go into the dictionary so you won't have to remove them:
struct A {
var first: String?
var second: String?
var third: String?
}
let a = A(first: "one", second: nil, third: "three")
let pairs: [(String, String?)] = [
("first", a.first),
("second", a.second),
("third", a.third)
]
var dictionary = [String : String]()
for (key, value) in pairs {
dictionary[key] = value
}
print(dictionary)
["third": "three", "first": "one"]
As @Hamish noted in the comments, you can use a DictionaryLiteral
(which internally is just an array of tuples) for pairs
which allows you to use the cleaner dictionary syntax:
let pairs: DictionaryLiteral<String,String?> = [
"first": a.first,
"second": a.second,
"third": a.third
]
All of the other code remains the same.
Note: You can just write DictionaryLiteral
and let the compiler infer the types, but I have seen Swift fail to compile or compile very slowly for large dictionary literals. That is why I have shown the use of explicit types here.
Alternatively, you can skip the Array
or DictionaryLiteral
of pairs
and just assign the values directly:
struct A {
var first: String?
var second: String?
var third: String?
}
let a = A(first: "one", second: nil, third: "three")
var dictionary = [String : String]()
dictionary["first"] = a.first
dictionary["second"] = a.second
dictionary["third"] = a.third
print(dictionary)
["third": "three", "first": "one"]