Adam S
Adam S

Reputation: 16414

How can I create an inline dictionary in Swift while omitting potentially nil optionals as values?

I have some optional properties (a small sample of the actual properties in the class, for brevity):

var assigneeIds: [Int]?
var locationIds: [Int]?
var searchQuery: String?

I need to convert these to JSON. I've been advised by the maintainer of the API to not supply keys with nil values, so my toJson method looks like this:

var result: [String: AnyObject] = [:]
if let assigneeIds = assigneeIds {
    result["assignee_ids"] = assigneeIds
}
if let locationIds = locationIds {
    result["location_ids"] = locationIds
}
if let searchQuery = searchQuery {
    result["search_query"] = searchQuery
}
return result

This doesn't feel very "Swift" - it's quite verbose. Ideally, I'd like it to look similar to the following - with the exception of it not setting the key if the optional has no value. The below code will still set the key, along with an NSNull value which gets serialised to "assignee_ids": null in the resulting JSON. Presumably I can configure the serializer to omit NSNull values, but I've encountered this pattern elsewhere and am interested to see if there's a better way.

return [
    "assignee_ids" : self.assigneeIds ?? NSNull(),
    "location_ids" : self.locationIds ?? NSNull(),
    "search_query" : self.searchQuery ?? NSNull()
]

How can I skip creating key->NSNull entries when building an inline dictionary in this way?

Upvotes: 1

Views: 319

Answers (2)

Danny Bravo
Danny Bravo

Reputation: 4658

Try this:

return [
    "assignee_ids" : self.assigneeIds ?? NSNull(),
    "location_ids" : self.locationIds ?? NSNull(),
    "search_query" : self.searchQuery ?? NSNull()
].filter { key, val -> Bool in !(val is NSNull) }

Upvotes: 2

Kametrixom
Kametrixom

Reputation: 14983

You actually don't need to unwrap the values at all, since the value won't be set in the dictionary when it's nil. Example:

var dict : [String : Any] = [:]

dict["a"] = 1      // dict = ["a" : 1]
dict["b"] = nil    // dict = ["a" : 1]
dict["a"] = nil    // dict = [:]

You can also CMD-Click on the bracket and look at the definition of the Dictionary subscript:

public subscript (key: Key) -> Value?

this means you get an Optional (whether it exists or not) and you set an Optional (to set it or remove it)

Your code will look like this:

var result: [String: AnyObject] = [:]
result["assignee_ids"] = assigneeIds
result["location_ids"] = locationIds
result["search_query"] = searchQuery
return result

Upvotes: 4

Related Questions