Swift 3: How to convert from [String: AnyObject] to [String: String]? - swift

I want to convert a [String: Anyobject] dictionary to [String: String] dictionary?
How can I do this?

You cannot convert a dictionary directly from [String: AnyObject] to [String: String]: since AnyObject can hold different types of values in the same dict, each such value entry isn't necessarily convertable to String.
Instead, you need to go over each key-value pair and conditionally perform a value conversion to String, if possible. E.g.:
// (example) source dict
let dict: [String: AnyObject] = ["foo": "1" as AnyObject,
"bar": 2 as AnyObject,
"baz": "3" as AnyObject]
// target dict
var targetDict = [String: String]()
for (key, value) in dict {
if let value = value as? String { targetDict[key] = value }
} // targetDict: ["foo": "1", "baz": "3"]

Simply, you can do it this way:
if let castedDict = dictionary as? [String: String] {
print("Converted successfully: \(castedDict)")
} else {
print("Failed to cast the dictionary")
}

Related

Update dictionary values in nested dictionary

The nested dictionary that I am using is build like this
var fDict : [String : Any] = [:]
var errors : [String : Any] = [:]
var error : [String : Any] = [:]
let base : [String : Any] = ["key": "123"]
error["error"] = base
errors["errors"] = error
fDict["profile-response"] = errors
The dictionary is looking like :
{
“profile-response“ : {
“errors” : {
“error” : {
“key“ = “123”
}
}
}
}
I have written code to update the value of key to "abc"
Code :
func replaceErrorWithCustomError( data : inout [String: Any]) {
for (key,value) in data {
if key == "key" {
data.updateValue("abc", forKey: key)
break
} else if var value = value as? [String: Any] {
replaceErrorWithCustomError(data: &value)
}
}
}
The result before update and after update remains same. Please suggest how to make changes in the current dictionary without taking another dictionary.
You can try this -
func replaceErrorWithCustomError(data: inout [String: Any]) {
func updateError(dict: inout [String: Any]) -> [String: Any] {
for (key, value) in dict {
if key == "key" {
dict.updateValue("abc", forKey: key)
break
} else if var value = value as? [String: Any] {
// This is the key change
// Result must be updated back into parent
dict[key] = updateError(dict: &value)
}
}
return dict
}
updateError(dict: &data)
}

How to update a value in a nested dictionary given path fragment in Swift?

I have a nested dictionary and an array containing path fragment. I need to update the value at that location.
I am possibly looking for a recursive function instead of extensions to Dictionary types and such.
I am not able to do this recursively because I making a copy of the inout param.
var dict: [String: Any] = ["channel": ["item": ["title": 1111]]]
var pathFrag = ["channel", "item", "title"]
var val = 123
func addAt(pathFrag: inout [String], val: Int, data: inout [String: Any]) {
if let first = pathFrag.first {
if let item = data[first] {
print(item)
pathFrag.remove(at: 0)
if !pathFrag.isEmpty {
var d: [String: Any] = data[first] as! [String: Any]
print("e: \(d)")
return addAt(pathFrag: &pathFrag, string: string, data: &d)
} else {
data[first] = val
print("else: \(data)") // ["title": 123]
}
}
}
}
addAt(pathFrag: &pathFrag, val: val, data: &dict)
print(dict)
How to update the value of title to 123?
Note that var d: [String: Any] = data[first] as! [String: Any] makes a copy of data[first] and not a reference to it like & and inout. So, when addAt(pathFrag: &pathFrag, string: string, data: &d) is called, only d is changed. To make this change reflect on the original data[first], you have to assign d back to data[first].
Do this:
var dict: [String: Any] = ["channel": ["item": ["title": 1111]]]
var pathFrag = ["channel", "item", "title"]
func addAt(pathFrag: inout [String], data: inout [String: Any]) {
if let first = pathFrag.first {
if let item = data[first] {
pathFrag.remove(at: 0)
if !pathFrag.isEmpty {
var d: [String: Any] = data[first] as! [String: Any]
addAt(pathFrag: &pathFrag, data: &d)
data[first] = d
} else {
data[first] = 123
}
}
}
}
addAt(pathFrag: &pathFrag, data: &dict)
print(dict)
This is not what you asked, but the entire premise here is unSwifty. The use of [String:Any] is a Bad Smell. It seems more like Objective-C. And indeed this whole thing is a one-liner in Objective-C:
NSMutableDictionary * d1 = [[NSMutableDictionary alloc] initWithDictionary: #{ #"title" : #1111 }];
NSMutableDictionary * d2 = [[NSMutableDictionary alloc] initWithDictionary: #{ #"item" : d1 }];
NSMutableDictionary * d3 = [[NSMutableDictionary alloc] initWithDictionary: #{ #"channel" : d2 }];
Okay, that was just to prepare the data. Here’s the one-liner:
[d3 setValue:#123 forKeyPath:#"channel.item.title"];
But I would question the entire nested dictionaries concept; it seems to me you’ve gone down a wrong well here and you need to stand back and rethink your data structure.

How to check if an object is dictionary or not in swift 3?

Tried the 'is' keyword.
// Initialize the dictionary
let dict = ["name":"John", "surname":"Doe"]
// Check if 'dict' is a Dictionary
if dict is Dictionary {
print("Yes, it's a Dictionary")
}
This will give an error saying "'is' is always true".
I just want to check if an object is a dictionary. It can be with any key any value pairs.
The key is hashable and it is not accepting the Any keyword.
If you want to check if an arbitrary object is a dictionary first of all you have to make the object unspecified:
let dict : Any = ["name":"John", "surname":"Doe"]
Now you can check if the object is a dictionary
if dict is Dictionary<AnyHashable,Any> {
print("Yes, it's a Dictionary")
}
But this way is theoretical and only for learning purposes. Basically it's pretty silly to cast up a distinct to an unspecified type.
If you just want to check if your object is Dictionary you can just do this:
if let dictionary = yourObject as? Dictionary{
print("It is a Dictionary")
}
Put this in a playground:
import UIKit
// Create a dictionary and an array to convert to and from JSON
let d = ["first": "Goodbye", "second": "Hello"]
let a = [1, 2, 3, 4, 5]
let dictData = try! JSONSerialization.data(withJSONObject: d, options: [])
let arrayData = try! JSONSerialization.data(withJSONObject: a, options: [])
// Now that we have set up our test data, lets see what we have
// First, some convenience definitions for the way JSON comes across.
typealias JSON = Any
typealias JSONDictionary = [String: JSON]
typealias JSONArray = [JSON]
// Lets see what we have
let dict = try! JSONSerialization.jsonObject(with: dictData, options: [])
let array = try! JSONSerialization.jsonObject(with: arrayData, options: [])
// testing functions
func isDictionary(_ object: JSON) -> Bool {
return ((object as? JSONDictionary) != nil) ? true : false
}
func isArray(_ object: JSON) -> Bool {
return ((object as? JSONArray) != nil) ? true : false
}
// The actual tests
isDictionary(dict) // true
isArray(dict) // false
isDictionary(array)// false
isArray(array) // true
JSON dictionaries arrays have specific types
I prefer using if let or guard let while parsing JSON objects
if let dict = jsonObject as? [AnyHashable:Any] {
print("Yes, it's a Dictionary")
}
guard let dict = jsonObject as? [AnyHashable:Any] else {
print("No, it's not a Dictionary")
return
}
Simply use is operator to check type conformance.
Here's a little snippet where I check if it's a Dictionaryor an Array
let dict = ["name":"John", "surname":"Doe"]
let array = [ "John", "Doe" ]
if dict is Dictionary<String, String>
{
print("Yes, it's a Dictionary")
}
else
{
print("No, It's other thing")
}
if array is Array<String>
{
print("Yes, it's a Array")
}
else
{
print("No, It's other thing")
}

Realm write / add with dictionaries

I have the following code:
var rawTypeArray = [[String: String]]()
rawTypeArray = NSArray(contentsOfFile: finalPathString) as! [[String: String]]
for eachType: [String: String] in rawTypeArray {
let setType = SetTypes(value: eachType)
try! realm.write {
realm.add(setType)
}
let all = realm.objects(SetTypes.self)
print (all)
}
the values of the dictionary are not passed to the realm object.
All the object are initialized with the default values. The dictionary values are ignored, what's wrong?

Swift 2: How can i assign a dictionary to AnyObject?

I was using swift 1.2 and everything was going fine. after upgrading my Xcode to 7 . I faced some weird problems.
My code was :
let postData : AnyObject = ["username":username , "password":password] ;
I need this variable to be AnyObject, because
let jsonObject : AnyObject = postData ;
let jsonString = JSONStringify(jsonObject)
let data1 = jsonString.dataUsingEncoding(NSUTF8StringEncoding)
let task1 = NSURLSession.sharedSession().uploadTaskWithRequest(request, fromData: data1) {
(Data, Response, Error) -> Void in
needs a Anyobject for post Data header.
The error is
Value of type '[String : String?]' does not conform to specified type 'AnyObject'
can any one help me?
The problem is your password variable is an Optional<String>. This means the conversion from Swift dictionary to AnyObject (I think it tries to convert it to an NSDictionary) will fail.
If you do
let postData : AnyObject = ["username":username , "password":password!]
It should work unless the password is nil (check that before creating the dictionary)
If you want to be able to have null passwords in the output, you can do this in your dictionary
let postData : [String : AnyObject] = ["username":username , "password":password ?? NSNull()]
The following works
let pw: String? = "pw"
let pw2: String? = nil
var foo: [String : AnyObject] = ["bar" : pw ?? NSNull(), "baz" : pw2 ?? NSNull()]
let data = try NSJSONSerialization.dataWithJSONObject(foo, options: NSJSONWritingOptions.PrettyPrinted)
let str = NSString(data: data, encoding: NSUTF8StringEncoding)!
print(str)
And prints
{
"baz" : null,
"bar" : "pw"
}
I assume you are passing parameters to a request which require key as String and value can be AnyObject. Try this:
let postData: [String: AnyObject] = ["username":username , "password":password]
Also make sure username and password are not optional, and in that case, use ! to unwrap them if you are sure values wil be there.
let postData: [String: AnyObject] = ["username":username!, "password":password!]
or you can use
let postData: [String: AnyObject] = ["username":username ?? "", "password":password ?? ""]