Checking for Nil Value in Swift Dictionary Extension

Generic Swift Dictionary Extension for nil filtering

Update: As of Swift 5 this would be:

let filtered = dict.compactMapValues { $0 }

Update: As of Swift 4, you can simply do

let filtered = dict.filter( { $0.value != nil }).mapValues( { $0! })

It is currently being discussed if Dictionary should get
a compactMapValues method which combines filter and mapValues.


(Previous answer:)
You can use the same "trick" as in How can I write a function that will unwrap a generic property in swift assuming it is an optional type? and Creating an extension to filter nils from an Array in Swift:
define a protocol to which all optionals conform:

protocol OptionalType {
associatedtype Wrapped
func intoOptional() -> Wrapped?
}

extension Optional : OptionalType {
func intoOptional() -> Wrapped? {
return self
}
}

Then your dictionary extension can be defined as:

extension Dictionary where Value: OptionalType {
func filterNil() -> [Key: Value.Wrapped] {
var result: [Key: Value.Wrapped] = [:]
for (key, value) in self {
if let unwrappedValue = value.intoOptional() {
result[key] = unwrappedValue
}
}
return result
}
}

Example:

let dict = ["mail": nil, "name": "John Doe"] // Type is [String : String?]
let filtered = dict.filterNil() // Type is [String : String]
print(filtered) // Output: ["name": "John Doe"]

Make a dictionary value non-optional as extension

Your method produces a dictionary of the same type [Key: Value]
with Value being some optional type. What you probably want is
to produce a dictionary of type [Key: Value.Wrapped]:

extension Dictionary where Value: OptionalType {

func jsonSanitize() -> [Key: Value.Wrapped] {
var newDict: [Key: Value.Wrapped] = [:]
for (key, value) in self {
if let v = value.asOptional {
newDict.updateValue(v, forKey: key)
}
}
return newDict
}
}

Example:

let dict: [String: Int?] = [
"foo": 1234,
"bar": nil
]
var dict2 = dict.jsonSanitize()
print(dict2) // ["foo": 1234]

Note also that of Swift 3.0.1/Xcode 8.1 beta, optionals
are bridged to NSNull instances automatically, see

  • SE-0140 – Warn when Optional converts to Any, and bridge Optional As Its Payload Or NSNull

Elegantly populate dictionary from a struct checking nil values

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

How to check for nil inside extension

Checking for nil there is not required.

That function is an instance function on String.

It can only ever be run on an instance of String.

Secondly Swift does not have "nil messaging" like Objective-C so the String instance that the function is called on HAS to be not nil. Even in Objective-C this would still not matter as the function would not run if called on a nil String.

So, the message is correct, Value of type "String" can never be nil.

Building a Dictionary extension in Swift to append to an array as value

Here is possible solution. Tested with Xcode 11.4

extension Dictionary {

mutating func add<T>(_ element: T, toArrayOn key: Key) where Value == [T] {
self[key] == nil ? self[key] = [element] : self[key]?.append(element)
}
}

Swift: How to remove a null value from Dictionary?

You can create an array containing the keys whose corresponding values are nil:

let keysToRemove = dict.keys.array.filter { dict[$0]! == nil }

and next loop through all elements of that array and remove the keys from the dictionary:

for key in keysToRemove {
dict.removeValueForKey(key)
}

Update 2017.01.17

The force unwrapping operator is a bit ugly, although safe, as explained in the comments. There are probably several other ways to achieve the same result, a better-looking way of the same method is:

let keysToRemove = dict.keys.filter {
guard let value = dict[$0] else { return false }
return value == nil
}

Dynamically remove null value from swift dictionary using function

Rather than using a global function (or a method), why not making it a method of Dictionary, using an extension?

extension Dictionary {
func nullKeyRemoval() -> Dictionary {
var dict = self

let keysToRemove = Array(dict.keys).filter { dict[$0] is NSNull }
for key in keysToRemove {
dict.removeValue(forKey: key)
}

return dict
}
}

It works with any generic types (so not limited to String, AnyObject), and you can invoke it directly from the dictionary itself:

var dic : [String: AnyObject] = ["FirstName": "Anvar", "LastName": "Azizov", "Website": NSNull(),"About": NSNull()]
let dicWithoutNulls = dic.nullKeyRemoval()

Check if dictionary contains value in Swift

Since you only want to check for existance of a given value, you can apply the contains method for the values properties of your dictionary (given native Swift dictionary), e.g.

var types: [Int : String] = [1: "foo", 2: "bar"]
print(types.values.contains("foo")) // true

As mentioned in @njuri: answer, making use of the values property of the dictionary can seemingly yield an overhead (I have not verified this myself) w.r.t. just checking the contains predicate directly against the value entry in the key-value tuple of each Dictionary element. Since Swift is fast, this shouldn't be an issue, however, unless you're working with a huge dictionary. Anyway, if you'd like to avoid using the values property, you could have a look at the alternatives given in the forementioned answer, or, use another alternative (Dictionary extension) as follows:

extension Dictionary where Value: Equatable {
func containsValue(value : Value) -> Bool {
return self.contains { $0.1 == value }
}
}

types.containsValue("foo") // true
types.containsValue("baz") // false


Related Topics



Leave a reply



Submit