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
Include Swiftui Views in Existing Uikit Application
Swift 5: What's 'Escaping Closure Captures Mutating 'Self' Parameter' and How to Fix It
Reference to Property in Closure Requires Explicit 'Self.' to Make Capture Semantics Explicit
Why .Pch File Not Available in Swift
Why Are Doubles Printed Differently in Dictionaries
Swift - Custom Setter on Property
How to Access Program Arguments in Swift
Get Button Pressed Id on Swift Via Sender
Programmatically Screenshot | Swift 3, Macos
Is Swiftui Backwards-Compatible With iOS 12.X and Older
Swiftui: Pop to Root View When Selected Tab Is Tapped Again
Whats the Swift Animate Withduration Syntax
How to Check Two Instances Are the Same Class/Type in Swift