Elegantly populate dictionary from a struct checking nil values

后端 未结 2 1865
温柔的废话
温柔的废话 2021-01-15 21:47

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

2条回答
  •  失恋的感觉
    2021-01-15 21:57

    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"]
    

提交回复
热议问题