Recursive Dictionaries in Swift 3 - ios

I have small structures like this:
let arguments = [ "a" : [ "b" : [ "c" : "d", "e" : "f", "g" : "h" ] ] ]
In Swift 2 I could pass them as NSDictionary parameter to other functions. Now its all error. Swift wants me to specify precisely the Dictionary types but since the structures vary its not possible. How to solve this?

Of course you can still pass a dictionary to a function in Swift 3.
If the dictionary structure is known
In this case, as suggested by sbarow just declare the dictionary type in the param of the function
let arguments = [ "a" : [ "b" : [ "c" : "d", "e" : "f", "g" : "h" ] ] ]
func foo(dict: [String: [String: [String: String]]]) {
print(dict)
}
foo(dict:arguments)
If the dictionary structure is unknown
In this case, if you only know your dictionary has a String as key then you can declare it like shown below
let arguments = [ "a" : [ "b" : [ "c" : "d", "e" : "f", "g" : "h" ] ] ]
func foo(dict: [String: Any]) {
print(dict)
}
foo(dict:arguments)

Related

Swift array map with comma before append new value

i have this following data structure in my Realm object
var tags = List<Tag>()
"tags": [
{
"tagId": "80069",
"tagName": "A"
},
{
"tagId": "80070",
"tagName": "B"
},
{
"tagId": "80071",
"tagName": "C"
},
{
"tagId": "80073",
"tagName": "D"
}
]
So what i want to achieve is, I map all my tag name into my new array
this is my code
let realmObject = self.realm.objects(MyDTO.self)
let array = Array(realmOutletList).map{Array($0.tags).map{$0.tagName!}.joined(separator: ",")}
it prints out this
["A,B,C", "A,C,D", "B,C,D"]
What I want to achieve is like
["A","B","C", "A","C","D", "B","C","D"]
I need that kind of array because I am going to create a Set from the array and then compare with another array
The compared array will be like
["A","B","C", "A","C","D", "B","C","D"]
because of the compared Array and the realmObject Array is different, it always shows false when i use
let subset = filterSet.isSubset(of: mySet)
Can anyone guide me please??
Thanks
Let's walk through solving the issue:
Consider that you have:
let originalArray = ["A,B,C", "A,C,D", "B,C,D"]
First, we need to separate each string in originalArray by "," character, so we could do:
let modifiedArray = originalArray.map { $0.components(separatedBy: ",") }
We map it to transform each string to a strings array (separation).
So far, the output of modifiedArray would be:
[["A", "B", "C"], ["A", "C", "D"], ["B", "C", "D"]]
which is an array of strings array.
Second, we need to spilt each -string- array in modifiedArray (having one reduced strings array instead), so we could do:
var final = [String]()
for array in modifiedArray {
for string in array {
final.append(string)
}
}
OR by using reduce
let finalArray = modifiedArray.reduce([], +)
Therefore, finalArray would be:
["A", "B", "C", "A", "C", "D", "B", "C", "D"]
which is the desired result.
Conclusion
For a fully one-lined answer (following the high-order functions approach):
let originalArray = ["A,B,C", "A,C,D", "B,C,D"]
let desiredArray = originalArray.map { $0.components(separatedBy: ",") }.reduce([], +)
print(desiredArray) // ["A", "B", "C", "A", "C", "D", "B", "C", "D"]
Well, it's quite easy:
This
let array = Array(realmOutletList).map{Array($0.tags).map{$0.tagName!}.joined(separator: ",")}
should be this
let array = Array(realmOutletList).flatMap{Array($0.tags).map{$0.tagName!}}
That's all. And you will get your ["A","B","C", "A","C","D", "B","C","D"].

Alamofire send Array of Dictionaries in a parameter

I have a POST API in which I send multiple parameters, one of those parameters has to be an array of dictionaries.
let arr = [
[
"id" : "1",
"price" : "10"
],
[
"id" : "2",
"price" : "20"
]
]
let params : Parameters = [
"param1" : "anyvalue1",
"param2" : "anyvalue2",
"param3" : arr,
]
When I use these parameters in Alamofire Request and hit the API, print(res.result.value) always returns unknown. Can anyone help me with this. Following is the way of requesting the API
Alamofire.request(url, method:.post, parameters: params).responseJSON{(res) in
print(res.result.value) //always shows 'unknown' as output
}
Try to make params a Dic of [String :Any ] like that :
let params : [String:Any] = [
"param1" : "anyvalue1",
"param2" : "anyvalue2",
"param3" : arr,
]

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 :)

JSON to Array conversion

I'm trying to convert a JSON string to an array. The JSON string is:
[
{
"field_value" : "28 Aug 2017",
"field_data_type_combo_value" : "",
"field_data_type_category_id" : "1",
"form_id" : "19",
"field_id" : "133",
"message_unique_id" : "7941501245582800298",
"field_data_type_combo_id" : "0",
"field_data_type_id" : "1"
},
{
"field_value" : "",
"field_data_type_combo_value" : "",
"field_data_type_category_id" : "9",
"form_id" : "19",
"field_id" : "134",
"message_unique_id" : "7941501245582714588",
"field_data_type_combo_id" : "0",
"field_data_type_id" : "26"
},
{
"field_value" : "hk",
"field_data_type_combo_value" : "",
"field_data_type_category_id" : "6",
"form_id" : "19",
"field_id" : "135",
"message_unique_id" : "7941501245582699681",
"field_data_type_combo_id" : "0",
"field_data_type_id" : "17"
}
]
and my conversion code is
if let data = text.data(using: .utf8) {
do {
return try JSONSerialization.jsonObject(with: data, options: .mutableContainers) as? [String : AnyObject]
} catch {
print(error.localizedDescription)
}
}
The conversion result is always nil. I also checked that JSON string in Online JSON Viewer and the string is correct. Please help me guys.
You explicitely write in your call:
as? [String: AnyObject]
In other words, you ask Swift to take whatever JSON returned, check that it is a dictionary with String keys, and either return that dictionary or nil. Since your data is an array and not a dictionary, it returns nil. Exactly what you asked for, but not what you wanted.
What you are expecting is an array of dictionaries, not a dictionary. You should also use Any instead of AnyObject. So convert it using
as? [[String: Any]]
The outer [] means it is an array, the inner [String: Any] means the items in the array must be dictionaries with String keys and Any values.
And why are you using .mutableContainers? If you have a good reason that you can explain then use it. If you don't and just copied the code from somewhere then remove it.
That json isn't a dictionary on the top level but an array. You can see that from the [ ... ], it would be a dictionary if it was { ... }. Fix your code using the corresponding cast:
return try JSONSerialization.jsonObject(with: data, options: .mutableContainers) as? [AnyObject]

swift: Add multiple <key, value> objects to NSDictionary

I'm trying to add multiple objects to NSDictionary, like
var myDict: NSDictionary = [["fname": "abc", "lname": "def"], ["fname": "ghi", "lname": "jkl"], ...]
Is it even possible to do this? If not, please suggest a better way. I actually need to convert this NSDictionary to JSON string and send that to the server, so I need multiple objects in NSDictionary.
You can definitely make a dictionary of dictionaries. However, you need a different syntax for that:
var myDictOfDict:NSDictionary = [
"a" : ["fname": "abc", "lname": "def"]
, "b" : ["fname": "ghi", "lname": "jkl"]
, ... : ...
]
What you have looks like an array of dictionaries, though:
var myArrayOfDict: NSArray = [
["fname": "abc", "lname": "def"]
, ["fname": "ghi", "lname": "jkl"]
, ...
]
To get JSON that looks like this
{"Data": [{"User": myDict1}, {"User": myDict1},...]}
you need to add the above array to a dictionary, like this:
var myDict:NSDictionary = ["Data" : myArrayOfDict]
SWIFT 3.0
List item
Frist of all you can create NSArray and Then you can Set Array in NSMutableDictionary using setvalue(forKey:) default method.
var arrFname : NSArray!
arrFname = ["abc","xyz","mno"]
var arrLname : NSArray!
arrFname = ["tuv","xuv","swift"]
var dicSet : NSMutableDictionary!
dicSet.setObject(arrFname, forKey : "Fname")
dicSet.setObject(arrLname, forKey : "Lname")
print(dicSet)
var tempDict = NSMutableDictionary()
tempDict.setValue("sam", forKey : "one")
print(tempDict["one"] ?? "1")

Resources