Elegantly populate dictionary from a struct checking nil values

后端 未结 2 1890
温柔的废话
温柔的废话 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 22:24

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

提交回复
热议问题