Check for nil values in Dictionary - Swift 3 - swift

I have converted a XML into a Dictionary and I would like to check first if any of the values is nil.
Is there any alternative to this ugly code (the Dictionary contains 30 key-value pairs):
if (xmlDict["key1" != nil && xmlDict["key2" != nil && .... && xmlDict["key30" != nil) {
//Do something with these values
} else {
//Do not do anything at all with this dictionary
}
[Update]
The dictionary has this format (mixed value types):
let futuramaDict = NSArray(contentsOf: xmlFile!) as? [NSDictionary]
futuramaDict {
"Cartoon" : "Futurama"
"Length" : 120
"MainCharacter" : "Philip J. Fry"
"ClosestFriends" : ["Bender","Leela","Zoidberg"]
}
I left the other key-value pairs so I don't fill the thread with irrelevant content

You can check with one line
xml.values.filter({ $0 == nil }).isEmpty
Here's the complete snippet
let xml: [String: String?] = [
"key1" : "Value1",
"key2" : "Value2",
"key3" : nil,
"key4" : "Value4"
]
if xml.values.filter({ $0 == nil }).isEmpty
{
print("Plenty of values")
}
else
{
print("nil :(")
}

A more "swifty" way:
for (key, val) in xmlDict where val == nil {
print("Found a nil value for this key: \(key)")
}

A quick and short solution:
if xmlDict.values.contains(where: {$0 == nil}) {
print("Dict contains at least one nil value")
}
In the case when you have a predefined set of keys you want to look for:
let keysToLookFor = ["k1", "k2", "k3", "k4"]
if keysToLookFor.contains(where: {xmlDict[$0] == nil}) {
print("Dict contains at least one nil value")
}
A sample dict for testing:
let xmlDict: [String: Any?] = ["k1": 22, "k2": Int(2), "k3": nil]

How about this -
let hasAnyKeyNil = dic.keys.contains(where: { dic[$0]! == nil })

This code should achieve what you want:
var values = [Any]()
for index in 1 ... 30 {
let key = "key\(index)" // Generates "key1" through "key30"
guard let value = xmlDict[key] else {
values.removeAll()
break // Abort loop
}
values.append(values)
}
// Next: Process contents of array 'values'
// (will be empty if any key was absent)

Related

In swift 5, how to retrieve the key of the second low value in a dictionary of [String: Float]?

I have a dictionary:
var mydict: [String: Float] = ["aze": 22.4, "lkh": 42.04, etc ... ]
How to retrieve the key of the second low value in mydict?
You can use sorted(by:) to sort the Dictionary into ascending order based on the values, then simply get the 2nd element from the sorted array of key-value pairs and access its key property.
var mydict: [String: Float] = ["aze": 22.4, "lkh": 42.04, "abc": 25.12 ]
let ascendingDict = mydict.sorted(by: { $0.value < $1.value })
let secondLowest = ascendingDict[1]
secondLowest.key // "abc"
Here is another solution that also handles dictionaries that contain 1 element or less by returning nil in that case.
The solution is using a tuple of optional tuples (key/value) and the second lowest key is then found by using reduce(into:) on the dictionary
let secondLowest = mydict.reduce(((String, Float)?, (String,Float)?)(nil, nil)) {
guard let first = $0.0 else {
return ($1, nil)
}
guard let second = $0.1 else {
if first.1 < $1.value { return (first, $1) }
return ($1, first)
}
return $1.value < first.1 ? ($1, first) : ($1.value < second.1 ? (first, $1) : $0)
}.1?.0
this could also be expressed in an extension to Dictionary as a computed property
extension Dictionary where Value: Comparable {
var keyForSecondLowest: Key? {
self.reduce(((Key, Value)?, (Key,Value)?)(nil, nil)) {
guard let first = $0.0 else {
return ($1, nil)
}
guard let second = $0.1 else {
if first.1 < $1.value { return (first, $1) }
return ($1, first)
}
return $1.value < first.1 ? ($1, first) : ($1.value < second.1 ? (first, $1) : $0)
}.1?.0
}
}
Example
if let secondLowest = mydict.keyForSecondLowest {
print(secondLowest)
}
Another approach is via pattern matching within reduce():
let minims = mydict.reduce(into: ((String, Float)?.none, (String, Float)?.none)) {
switch ($0.0?.1 ?? .infinity, $0.1?.1 ?? .infinity, $1.1) {
case let (min1, _, val) where val < min1: $0 = ($1, $0.0)
case let (_, min2, val) where val < min2: $0.1 = $1
default: break
}
}
print(minims.1?.0)
The algorithm is the same as the Joakim's one, and it works by computing a tuple of optional tuples, the first optional tuple corresponding to the minimal entry in the dictionary, while the second will correspond to the next minimal entry.
Note that if the dictionary has less than two elements then some of the result tuples will be nil.
Performance is linear, as it requires only one iteration of the dictionary.

Check for nil in dictionary

I have a class in my app, where the user inputs values and i set them to an instance of the class, then i upload this data to Database, but i have to convert the class to something the database accepts and i'm converting to dictionary using Mirror Reflection. Some properties in my class can be nil, because by design not all properties are required. But i can't pass nil values to the database.
I have recreated my example is very simplified playground. i didn't set a value for the name property of the class
I tried to check for nil before adding the key, value pair to the dictionary
Below is my code
import UIKit
class Color: NSObject {
var name: String?
var code: Int?
var shade: String?
}
let cl = Color()
cl.code = 3456
cl.shade = "DARK"
var colorDict = [String: Any]()
for x in Mirror(reflecting: cl).children.makeIterator() {
if let val = x.value as Any? {
print(type(of: val))
colorDict[x.label!] = val
}
}
print (colorDict)
the output in console is as below
Optional<String>
Optional<Int>
Optional<String>
["name": nil, "code": Optional(3456), "shade": Optional("DARK")]
how can i check for nil values and skip adding that property to the Dictionary
i have tried to loop through the dictionary after i add all values including nils and check for values too but i get the below warning
Comparing non-optional value of type 'Any' to nil always returns false
declaring the dictionary as below
var colorDict = [String: Any?]()
for x in colorDict {
if x.value == nil {
colorDict.removeValue(forKey: x.key)
}
}
removes the warning but it doesn't remove anything.
I would really appreciate your help.
The way of unwrapping objects of type Any that contain optionals is kind of weird but you can check that the values aren't nil in your mirror like this:
for x in Mirror(reflecting: cl).children {
if case Optional<Any>.some(let val) = x.value {
print(type(of: val))
colorDict[x.label!] = val
}
}
You can do this really easily in a one-liner, using filter:
let dict: [String : Any?] = ["Foo" : 3, "Bar" : nil, "Baz" : "Qux"]
let noNils = dict.filter { $0.value != nil }
print(noNils) // prints ["Foo": Optional(3), "Baz": Optional("Qux")]
As i have suggested, initialise all values.
If you decide not to store the nil values you will end up with children that some of them will have 1, some 2 and some 3 nodes, nothing wrong with that, BUT what happens when you go to read them?
You havent shared any info as to how these values will be used by the app, but assuming you have one function to read the properties/nodes of stored colors, it will go to read all 3 :
ref.child("colors").child("someSpecificColor").observeSingleEvent(of: .value, with: { (snapshot) in
// Get color values
let value = snapshot.value as? NSDictionary
let name = value?["name"] as? String ?? ""
let code = value?["code"] as? String ?? ""
let shade = value?["shade"] as? String ?? ""
// ...
}) { (error) in
print(error.localizedDescription)
}
See the issue?
Here is a simple solution :
var colorDict = [String: Any?]()
for x in Mirror(reflecting: cl).children.makeIterator() {
if let val = x.value, val != nil {
print(type(of: val))
colorDict[x.label!] = val
}
}
Here before to print and add you val, you check if the val is different than nil. As your output suggests in your console log you print :
Optional<String>
Optional<Int>
Optional<String>
val is an optional. So, if it's nil it won't be added. If not, you enter into the if statement and that's it.

Check if dictionary contains value in Swift

Just simple task. I've got a dictionary var types = [Int : String]() which inits like an empty and after some user actions it fills with data. According to emptiness or some specific data in this dictionary I enable/disable a button in UI.
Check for emptiness is easy, but how to check if dictionary contains certain value?
Compiler suggested me a placeholder with predicate:
types.contains(predicate: ((Int, String)) throws -> Bool>)
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
I wrote a function which is using contains method on dictionary.
Your specific case:
let dic : [Int : String] = [1 : "a", 2 : "b"]
func dictionary(dict : [Int : String], containsValue value : String)->Bool{
let contains = dict.contains { (_,v) -> Bool in
return v == value
}
return contains
}
let c = dictionary(dic, containsValue: "c") // false
let a = dictionary(dic, containsValue: "a") // true
Generic:
extension Dictionary{
func containsValue<T : Equatable>(value : T)->Bool{
let contains = self.contains { (k, v) -> Bool in
if let v = v as? T where v == value{
return true
}
return false
}
return contains
}
}
I've tested this function against dictionary.values.contains() and it is roughly two times faster.
If you want to check if already contains a value this would be the way:
if !yourDictionary.values.contains("Zero") {
yourDictionary[newItemKey] = newItemValue; //addNewItem
}
else {
print("this value already exists");
}
And this one if you want to check if the key exists:
You get the item to add to your dictionary.
Check if the item's key already exists
If it doesn't, append the item or enable the button.
//1
let newItemKey = 0
let newItemValue = "Zero"
//2
let keyExists = yourDictionary[newItemKey] != nil
//3
if !keyExists {
yourDictionary[newItemKey] = newItemValue; //addNewItem
}
else {
print("This key already exists");
}
The dictionary getter returns an optional value.
let dictionary = ["ben": "says hi"]
let containsAlpha = dictionary["alpha"] != nil
let containsBen = dictionary["ben"] != nil

Swift Dictionary: join key and value into string

I'm looking for a best syntax for this:
let responseParameters = ["keyA" : "valueA", "keyB" : "valueB"]
var responseString = ""
for (key, value) in responseParameters {
responseString += "\(key):\(value)"
if Array(responseParameters.keys).last != key {
responseString += "+"
}
}
//responseString: keyA:valueA+keyB:valueB
Something like an array joinWithSeparator, using a flatMap or something like that. (study purpose)
You can map over key/value pairs in dictionaries to convert them to an Array of Strings, then you can join those with +. But remember, dictionaries are unordered, so this will not preserve the input ordering.
let responseParameters = ["keyA" : "valueA", "keyB" : "valueB"]
let responseString = responseParameters.map{ "\($0):\($1)" }
.joined(separator: "+")
A dictionary is not an ordered collection, so you'll have to sort the keys prior to accessing the "ordered version" of the key-value pairs. E.g.
let responseParameters = ["keyA" : "valueA", "keyB" : "valueB", "keyC" : "valueC"]
let responseString = responseParameters
.sort { $0.0 < $1.0 }
.map { $0 + ":" + $1 }
.joinWithSeparator("+")
print(responseString) // keyA:valueA+keyB:valueB+keyC:valueC
Updated answer for swift 5 :
let responseParameters = ["keyA": "valueA", "keyB": "valueB"]
let responseString = responseParameters.map { "\($0):\($1)" }
.joined(separator: "+")
Actually, you can use reduce like: 🙌
let responseParameters = ["keyA": "valueA", "keyB": "valueB"]
let responseString = responseParameters.reduce("") { $0.isEmpty ? "\($1.key):\($1.value)" : "\($0)+\($1.key):\($1.value)" }

swift reflection causes impossible nil value for any

I'm trying to use swift reflection to check for changes in objects so I can send only changed properties up to the server. Some of my properties are optional. To compare those values, I need to unwrap them but, of course, you can ONLY unwrap actual values, not nil values. So, I need to check if one of the values is nil before I compare them.
In my playground, I tried the following:
import UIKit
class myClass
{
var fieldOne:String?
var fieldTwo:Int?
var fieldThree:Float?
}
var oneMyClass = myClass()
oneMyClass.fieldOne = "blah"
oneMyClass.fieldThree = 3.5
var oneOtherClass = myClass()
oneOtherClass.fieldOne = "stuff"
oneOtherClass.fieldTwo = 3
let aMirror = Mirror(reflecting: oneMyClass)
let bMirror = Mirror(reflecting: oneOtherClass)
for thing in aMirror.children
{
for thing2 in bMirror.children
{
if thing.label! == thing2.label!
{
print("property: \(thing.label!)")
print("before: \(thing.value)")
print("after: \(thing2.value)")
print("")
//let myTest = thing.value == nil ? "nil" : "not nil"
}
}
}
And it generates the following output:
property: fieldOne
before: Optional("blah")
after: Optional("stuff")
property: fieldTwo
before: nil
after: Optional(3)
property: fieldThree
before: Optional(3.5)
after: nil
As you can see, the expected properties are displayed as "nil". However, if you uncomment the let statement, you get an error stating:
playground52.swift:37:38: error: value of type 'Any' (aka 'protocol<>') can never be nil, comparison isn't allowed
And yet, we know from the output that it IS nil. How can this be and what can I do about it?
Based on this answer, I recommend using if case Optional<Any>.some(_).
For example:
aMirror.children.forEach {
guard let propertyName = $0.label else { return }
if case Optional<Any>.some(_) = $0.value {
print("property: \(propertyName) is not nil")
} else {
print("property: \(propertyName) is nil")
}
}
Thats look like some sort of bug. Look at that
let x = childMirror.value == nil ? "Nil" : "Not Nil" //dont compile.
let y = { (value:Any?) in
return value == nil ? "Nil" : "Not Nil"
}
let z = y(childMirror.value) //compile, but doesn't evaluate.
I guess the problem is because Any can store a Optional, but can't be wrapped around one. Try this:
func getValue(unknownValue:Any) -> Any {
let value = Mirror(reflecting: unknownValue)
if value.displayStyle != .Optional || value.children.count != 0 {
return "Not Nil"
} else {
return "Nil"
}
}