ValueForKeyPath() returning something unexpected - ios

I am parsing this json:
{
"destination_addresses" : [ "Göteborg, Sverige" ],
"origin_addresses" : [ "Stockholm, Sverige" ],
"rows" : [
{
"elements" : [
{
"distance" : {
"text" : "467 km",
"value" : 466568
},
"duration" : {
"text" : "4 timmar 34 min",
"value" : 16468
},
"status" : "OK"
}
]
}
],
"status" : "OK"
}
Into an NSDictionary like this:
let dataDict = NSJSONSerialization.JSONObjectWithData(data.dataUsingEncoding(NSUTF8StringEncoding), options: nil, error: nil) as NSDictionary
let distance = dataDict.valueForKeyPath("rows.elements.distance.value")
Now, this works, no crashes or errors. But, I expect it to return 466568 but instead it returns this:
(
(
466568
)
)
I dont know why and I cant use it and cant cast it into anything :(
There is another question similar to this, but the answer there didnt help me because Im not the one to change the json, Google is. So please no hate.

valueForKeyPath is returning a nested NSArray. You might want to cast it twice:
let distanceValue = ((distance as NSArray).objectAtIndex(0) as NSArray).objectAtIndex(0) as NSNumber
// or
let distanceValue = (distance as [NSArray])[0][0] as NSNumber
The distance value can't be directly accessed because valueForKeyPath does't implement a selector for array items.
Without valueForKeyPath:
let rows = dataDict.objectForKey("rows") as [NSDictionary]
let elements = rows[0]["elements"] as [NSDictionary]
let distance = elements[0]["distance"] as NSDictionary
let value = distance["value"] as NSNumber

This may be because rows is giving you array instead of Dictionary. So first get the value of rows which will give you an array. Get the object at zero index of your array and assign it to Dictionary and then try to get elements.distance.value

Related

How to get one value from a Codable struct with multiple values?

I have a large JSON response and inside
{
"availabilityResultList": [
{
"availabilityRouteList": [
{
"availabilityByDateList": [
{
"originDestinationOptionList": [
there are 3 separate
"originDestinationOptionList": [
{
"fareComponentGroupList":[...]
},
{
"fareComponentGroupList":[...]
},
{
"fareComponentGroupList":[...]
},
],
I can access the values in the first 'fareComponentGroupList' with Codables
as
root.availabilityResultList.first?.availabilityRouteList.first?.availabilityByDateList.first?.originDestinationOptionList.first?.fareComponentGroupList.first?.xxx
How do I access values the second and third fareComponentGroupList ?
(I am sorry about these silly questions but I am new with swift Codables)
Since originDestinationOptionList returns an array of dictionary, just fetch it from there by index.
let originDestinationOptionList = root.availabilityResultList.first?.availabilityRouteList.first?.availabilityByDateList.first?.originDestinationOptionList
let firstobject = originDestinationOptionList[0]["fareComponentGroupList"]
let secondObject = originDestinationOptionList[1]["fareComponentGroupList"]
let firstObjectsFirstItem = firstObject[0]
If the above gives error, this works ( Swift 5)
let originDestinationOptionList = root.availabilityResultList.first?.availabilityRouteList.first?.availabilityByDateList.first?.originDestinationOptionList
let firstobject = originDestinationOptionList[0].fareComponentGroupList.first
let firstObjectsFirstItem = firstObject?. (add the remaining part)

Adding to dictionary dynamically

I 'm having an array of dictionary like so...
[
{
"id" : "3",
"sellingPrice" : "520",
"quantity" : "15"
},
{
"id" : "5",
"sellingPrice" : "499",
"quantity" : "-1"
},
{
"id" : "8",
"sellingPrice" : "500",
"quantity" : "79"
}
]
Now I want to add to the dictionary another key called remaining_balance with a value of 420,499 & 500 respectively. How can I achieve this..? Hope somebody can help...
It seems like you want to add a value to your dictionary that is an array:
var arrDict = Array<Dictionary<String,Any>>() //Your array
arrDict.append(["id":"3","sellingPrice":"520","quantity":"13"])
arrDict.append(["id":"5","sellingPrice":"43","quantity":"32"])
arrDict.append(["id":"8","sellingPrice":"43","quantity":"33"])
let arrValue = ["420","499","500"] //Your remaining value in array
print("Before ",arrDict)
for (index,dict) in arrDict.enumerated() {
var dictCopy = dict //assign to var variable
dictCopy["remaining_balance"] = arrValue[index]
arrDict[index] = dictCopy //Replace at index with new dictionary
}
print("After ",arrDict)
EDIT
If you are able keep an index of an array it would be possible,
Assuming that you have the index of an array
var dictCopy = arrDict[index]
dictCopy["remaining_balance"] = "666" //Your calculated value
arrDict[index] = dictCopy //Replace at index with new dictionary
var newKV = [["remaining_balance": "420"],["remaining_balance": "490"],["remaining_balance": "500"]]
let array = [["id":"3", "sellingPrice":"520", "quantity":"15"], ["id":"5", "sellingPrice":"520", "quantity":"15"], ["id":"8", "sellingPrice":"520", "quantity":"15"]]
let newArray = array.enumerated().map { (index : Int, value: [String: String]) -> [String: String] in
var dic = value
dic.merge(newKV[index]) { (_, new) -> String in
new
}
return dic
}
You could achieve it by mapping your array:
var myArray = [["id": "3", "sellingPrice": "520", "quantity" : "15"], ["id": "5", "sellingPrice": "499", "quantity" : "-1"], ["id": "8", "sellingPrice": "500", "quantity" : "79"]]
print(myArray)
/*
[["id": "3", "sellingPrice": "520", "quantity": "15"],
["id": "5", "sellingPrice": "499", "quantity": "-1"],
["id": "8", "sellingPrice": "500", "quantity": "79"]]
*/
print("___________________")
var remainingBalanceDesriedValue = 420
myArray = myArray.map { (dict: [String: String]) -> [String: String] in
var copyDict = dict
copyDict["remaining_balance"] = "\(remainingBalanceDesriedValue)"
remainingBalanceDesriedValue = (remainingBalanceDesriedValue == 420) ? 499 : (remainingBalanceDesriedValue == 499) ? 500 : 420
return copyDict
}
print(myArray)
/*
[["sellingPrice": "520", "quantity": "15", "id": "3", "remaining_balance": "420"],
["sellingPrice": "499", "quantity": "-1", "id": "5", "remaining_balance": "499"],
["sellingPrice": "500", "quantity": "79", "id": "8", "remaining_balance": "500"]]
*/
Let's assume you have an array of dictionaries like so:
var arrayOfDictionaries = [
[
"id": 3,
"sellingPrice": 520,
"quantity": 15
]
]
It is important that arrayOfDictionaries is not a let constant, otherwise it is considered immutable and you can not call append on it.
Now you init a new dictionary like:
let newDictionary = [
"id": 10,
"remaining_balance": 420,
"quantity": 15
]
Now add the newDictionary like
arrayOfDictionaries.append(newDictionary)
If the order is important
If the order is important there are a couple of ways to go about that.
When calling append the new value (in this case the new dictionary) will always be inserted at the bottom of the array.
If for some reason you can not call append in the correct order you could use insert, which inserts your dictionary at a specific position.
Yet another way is to append the values wildly and after you are done, call sort on the array.
Improvement Tips
Notice that for the values I did not use strings, as you only have numbers like "id" : 30.
Also, if you want the second key to be called remaining_balance you should call the first key selling_price instead of sellingPrice. Because of conistency.
Alternative approach
As far as I have understood you are trying to implement some software that is responsibly for selling some products.
I think you are tackling this problem from a completely wrong side.
I think you should read about database relationships. Selling products actually is a very common problem.
Maybe this will help you. I would offer a possible solution myself, but I think this misses the point of your question.
If you decide to use the database approach, you won't necessarily have to use a database. You can take the approach and implement it using simple structs/classes/arrays.
I noticed this question lacks an extension answer, yes.. I'm gonna be that guy, so here it is. This could be made more generic by supporting other types of dictionaries, feel free to pitch in ;)
Inspiration from #eason's answer.
var newKV = [["remaining_balance": "420"],["remaining_balance": "490"],["remaining_balance": "500"]]
var array = [["id":"3", "sellingPrice":"520", "quantity":"15"], ["id":"5", "sellingPrice":"520", "quantity":"15"], ["id":"8", "sellingPrice":"520", "quantity":"15"]]
extension Array where Element == [String: String] {
enum UniquingKeysStrategy {
case old
case new
}
mutating func merge(with target: Array<Element>, uniquingKeysWith: UniquingKeysStrategy = .new) {
self = self.merged(with: target)
}
func merged(with target: Array<Element>, uniquingKeysWith strategy: UniquingKeysStrategy = .new) -> Array<Element> {
let base = self.count > target.count ? self : target
let data = self.count > target.count ? target : self
return data.enumerated().reduce(into: base, {
result, data in
result[data.offset]
.merge(data.element, uniquingKeysWith: {
old, new in
if strategy == .new { return new }
return old
})
})
}
}
let mergedArrays = newKV.merged(with: array, uniquingKeysWith: .old)
array.merge(with: newKV)
Happy Coding :)

how to add an object into a NSDictionary with respect to a key

I have NSDictionary, now I am trying to add an object in a particular key.How can I do that ? below is my code.
let businessPhoneDic = ["locationname" : "",
"area" : "",
"number" : "",
"type" : "",
];
let emailDic:NSMutableDictionary? = ["email" : "",];
let businessPhoneDic2 = ["locationname" : "hello",
"area" : "",
"phonenumber" : "",
"type" : "",
];
var mainDictionary = ["businessPhone" : businessPhoneDic as AnyObject,"email" : emailDic as AnyObject,
];
Now I want to add "businessPhoneDic2" into mainDictionary for key "businessPhone".How can I do that into Swift 3
By definition, you can't have two values mapped to the same key. The following code will replace the old key:
Swift 3
mainDictionary["businessPhone"] = businessPhoneDic2
Just use a new key if you need both values in your dictionary, or maybe store an array of dictionaries as values as such:
var mainDictionary : [String : [NSDictionary]] = ["businessPhone" : [businessPhoneDic]]
Then:
mainDictionary["businessPhone"]?.append(businessPhoneDic2)
First of all do not use MSMutable... collection types in Swift.
Use native Swift Dictionary
let emailDic = ["email" : ""]
Second of all annotate a dictionary with different types as [String:Any]
var mainDictionary : [String:Any] = ["businessPhone" : businessPhoneDic, "email" : emailDic]
If the value for key businessPhone is an array you can append the value. If it's a single dictionary you have to create an array.
This code considers both cases:
let businessPhone = mainDictionary["businessPhone"]
if var phone = businessPhone as? [[String:Any]] {
phone.append(businessPhoneDic2)
mainDictionary["businessPhone"] = phone
} else if let phone = businessPhone as? [String:Any] {
mainDictionary["businessPhone"] = [phone, businessPhoneDic2]
}
Try this :
mainDictionary["your key"] = businessPhoneDic2 as AnyObject // cast as per suggestions
Swift 3.x
// Initialize the Dictionary
var dict = ["name": "Foo", "surname": "Bar"]
// Add a new key with a value
dict["email"] = "foo.bar#email.com"
print(dict)

Swift access fields in nested dictionary

I've got to deal with a Dictionary like this, or more nested.
How can I access fields like "twotwo" ? Or is there any better possibility to model such structure?
let nestedDict = [
"fieldOne": "name",
"fieldTwo": "name",
"fieldThree":
[
[
"twoOne": "some text",
"twoTwo": true,
"twoThree": 1e-40
],
[
"twoOne": "some text",
"twoTwo": true,
"twoThree": 1e-40
]
]
]
nestedDict is a Dictionary, you get fieldThree with
let fieldThree = nestedDict["fieldThree"] as! [[String:Any]] // [[String:AnyObject]] in Swift 2 and lower.
fieldThree is an Arrayof [String:AnyObject] dictionaries, you get the value of twoTwo of the first array item with
let twoTwo = fieldThree[0]["twoTwo"] as! Bool
You can even retrieve all values of key twoTwo in the array
let allTwoTwo = fieldThree.map { $0["twoTwo"] as! Bool }

Swift: Could not cast value of type '__NSCFArray' to 'NSDictionary'

I have JSON data from website. I made the main dictionary and I can parse every data except one sub dictionary. I get the error "Swift: Could not cast value of type '__NSCFArray' to 'NSDictionary'"
This example of my data. I cannot parse "weather" but I can parse all other dictionaries like "wind".
["name": Mountain View, "id": 5375480, "weather": (
{
description = "sky is clear";
icon = 01n;
id = 800;
main = Clear;
}
), "base": cmc stations, "wind": {
deg = "129.502";
speed = "1.41";
Snippet of code
let windDictionary = mainDictionary["wind"] as! [String : AnyObject
let speed = windDictionary["speed"] as! Double
print(speed)
let weather = mainDictionary["weather"] as! [String : AnyObject]
print(weather)
on behalf your comment...I would say windDictionary is Dictionary...
Dictionary denotes in JSON with {} and
Array denotes with [] // In printed response you may have array with ()
So, your weather part is Array of Dictionary...You have to parse it like
let weather = mainDictionary["weather"] as! [[String : AnyObject]] // although please not use force unwrap .. either use `if let` or `guard` statement

Resources