I have a multidimensional dictionary and I am trying to add data into it without deleting data, but overwrite if duplicate.
var items = [Int: AnyObject]()
var IdsAndDetails = [String:AnyObject]()
let index = 3 // static for test
...
for result in results {
// result is String (result also indicates itemId)
let details = self.IdsAndDetails[result]
// details is AnyObject like [String:String]
if let itemDetails = details {
// Here I want to append data into 'items' variable
// This doesn't work: (Error-1)
self.items[index]![result] = itemDetails
}
(Error-1):
Cannot assign to immutable expression to type AnyObject.
However, if I try like, it works but it's not the approach I want. It's re-creating the dictionary. Instead, I want to append the data.
self.items = [
index : [result : itemDetails]
]
The structure of dictionary I want to get in the end is:
items = [
index : [
"id1" : ["key": "value", "key": "value"],
"id2" : ["key": "val", "key": "val"],
],
index : [
"id3" : ["key": "val", "key": "val"],
"id4" : ["key": "val", "key": "val"],
"id5" : ["key": "val", "key": "val"],
]
]
// index is Integer
// [Key:Value] is [String:String] - itemDetails equal to all `[key:value]`s
// 'id' is also String
Update: I also tried, but no luck
let a = self.items[index]
a![result]! = itemDetails as [String:String]
Update 2:
let valueDict = (value as! NSDictionary) as Dictionary
for (key, val) in valueDict {
let keyString = key as! String
let valString = val as! String
self.items[index]![result]![keyString]! = valString
}
But it's throwing error:
fatal error: unexpectedly found nil while unwrapping an Optional value
But Surprisingly debugging shows all values:
po index : 1
po itemId : "123123"
po keyString: "keyInString"
po valString: "valInString"
Update 3:
for index in 1...5 {
var results = [String]()
// First I retrieve nearby users and assign it to key
let itemsRef = Firebase(url: self.secret + "/items")
eventsRef.queryOrderedByChild("user_id").queryEqualToValue(key).observeEventType(.ChildAdded, withBlock: { snapshot in
// ^ above 'key' is the user_id retrieved before
let itemDetails = snapshot.value // item details - [key:val, key:val]
let itemId = snapshot.key // item ids [id1,id2,id3]
// I used 'KeyAndDetails' to store all values with ids
let IdsAndDetails = [itemId: itemDetails]
self.itemIdsArray = []
self.itemIdsArray.append(itemId)
if index == 1 {
// self.items = [
// index : [itemId : itemDetails]
// ]
// ^ This worked and gave me the structure
// but I don't want to overwrite it, instead, I want to append
// on the dictionary
// This is where I am trying to append into `self.items`,
// and throws error:
self.items[index]?[result] = (eventDetails as! [String : String])
}
...
It seems like you're trying to bypass Swift's type system instead of working with it. Instead of using AnyObject, you should be trying to use the exact type you want for the value of that dictionary. In this case it looks like you want something like [Int: [String: [String: String]]] (although, like #EricD said in the comments, you should probably be using a struct instead, if at all possible).
Here's a quick (static) example similar to the code in your question:
var items = [Int: [String: [String: String]]]()
let idsAndDetails = ["id2": ["key3": "value3"]]
let index = 3
items[index] = ["id1": ["key1": "value1", "key2": "value2"]]
let result = "id2"
if let itemDetails = idsAndDetails[result] {
items[index]?[result] = itemDetails
}
At the end of that, items will be:
[3: ["id1": ["key1": "value1", "key2": "value2"], "id2": ["key3": "value3"]]]
The ? in items[index]?[result] tells Swift to make sure items[index] is non-nil before attempting to execute the subscript method. That way, if you try to update an index that doesn't exist in items you don't cause a crash.
Related
I am having an array of dictionaries with columnId and columnValue as a pair. Now i need to flatten it as columnId as the key and columnValue as the value of it. How is it possible to do with swift higher order functions?
let arrayOfDictionaries = [["columnId": 123, "columnValue": "sample text"], ["columnId": 124, "columnValue": 9977332]]
//The end result should be:
flattenedDictionary: [String: Any] = ["123": "sample text", "124": 9977332]
Note: Result dictionary will be in the form of [String: Any]
This would work:
func flatten(_ pairs: [[String: Any]]) -> [String: Any] {
pairs.reduce(into: [String: Any]()) {
if let id = $1["columnId"] as? Int, let value = $1["columnValue"] {
$0["\(id)"] = value
}
}
}
You can do this in two steps;
Convert your input array into a sequence of key-value pairs using compactMap
Convert the sequence back into a dictionary using Dictionary(uniqueKeysWithValues:)
let arrayOfDictionaries = [["columnId": 123, "columnValue": "sample text"], ["columnId": 124, "columnValue": 9977332]]
let tupleArray:[(String,Any)] = arrayOfDictionaries.compactMap { dict in
guard let id = dict["columnId"], let value = dict["columnValue"] else {
return nil
}
return ("\(id)",value)
}
let flattenedDictionary: [String: Any] = Dictionary(uniqueKeysWithValues: tupleArray)
Note that this code will throw an exception if there are duplicate keys. You should either take steps to ensure the columnId values are unique or use Dictionary(keysAndValues:, uniquingKeysWith:) to resolve id clashes.
I need to create a dictionary from array with custom type for first index of the array.
Sample array : ["ABC","ZYZ","123"]
Required result : [{"name" : "ABC", "type:"A"},{"name" : "ZYZ", "type:"B"},{"name" : "123", "type:"B"}]
Note type A for first index.
My code
for url in urlArray {
urlDict["name"] = url
}
You can do a map, and then individually change the type of the first dictionary:
var dicts = urlArray.map { ["name": $0, "type": "B"] }
dicts[0]["type"] = "A"
Seeing how all your dictionary keys are all the same, and that you are sending this to a server, a Codable struct might be a better choice.
struct NameThisProperly : Codable {
var name: String
var type: String
}
var result = urlArray.map { NameThisProperly(name: $0, type: "B") }
result[0].type = "A"
do {
let data = try JSONDecoder().encode(result)
// you can now send this data to server
} catch let error {
...
}
I suppose you can use a high order function such as map or reduce
Here is an example using reduce
var array = ["ABC","ZYZ","123"]
var result = array.reduce([[String: String]](), { (previous, current) -> [[String: String]] in
let type = previous.count == 0 ? "A" : "B"
let dictForCurrent = [
"name": current,
"type": type
]
return previous + [dictForCurrent]
})
print(result)
The result:
[["type": "A", "name": "ABC"], ["type": "B", "name": "ZYZ"], ["name":
"123", "type": "B"]]
Use reduce to convert array to dictionary:
let resultDict: [String: String]
= array.reduce(into: [:]) { dict, url in
dict["name"] = url
}
The result will look like:
[
"name": URL1,
"name": URL2
]
Use map(_:) to convert each element of the array to dictionary like so,
let arr = ["ABC","ZYZ","123"]
let result = arr.map { (element) -> [String:String] in
var dict = [String:String]()
dict["name"] = element
if let char = element.first {
dict["type"] = String(char)
}
return dict
}
print(result)
since you are concern about the index, my approach will be using enumerated() which gives out the index
let array = ["ABC","ZYZ","123"]
var results: [[String: String]] = []
for (i, content) in array.enumerated() {
let type: String = i == 0 ? "A" : "B"
results.append(["name": content, "type": type])
}
print(result)
// [["type": "A", "name": "ABC"], ["name": "ZYZ", "type": "B"], ["type": "B", "name": "123"]]
I tried to append an array of dictionaries which are coming from server to a globally declared array.but i am getting an error like "Cannot convert value of type '[Any]' to expected argument type '[String : Any]'"if anyone helps me would be great.Thanks in advance
var pro = [[String:Any]]()
var productsdetails = Array<Any>()
productsdetails = userdata.value(forKey: "products") as! Array
print("response\(productsdetails)")
self.pro = self.pro.append(productsdetails)
print(self.pro)
Use this code like below, i hope this works
var pro = [[String:Any]]()
if let productsdetails = userdata.value(forKey: "products") as? [[String:Any]] {
print("response\(productsdetails)")
self.pro.append(productsdetails)
print(self.pro)
}
to solve this iterate
var pro = [[String:Any]]()
if let productsdetails = userdata.value(forKey: "products") as? [[String: Any]] {
for details in productsdetails {
pro.append(details)
}
}
or you may use directly self.pro = productsdetails if you not to want iterate
**image shows [String : String] as I have used [String : String] instead of [String : Any]*
You can try this: (Swift-4.2)
var data: [String: Any] = [
"key1": "example value 1",
"key2": "example value 2",
"items": []
]
for index in 1...3 {
let item: [String: Any] = [
"key": "new value"
]
// get existing items, or create new array if doesn't exist
var existingItems = data["items"] as? [[String: Any]] ?? [[String: Any]]()
// append the item
existingItems.append(item)
// replace back into `data`
data["items"] = existingItems
}
Answer same as: Append to array in [String: Any] dictionary structure
Can you show us your response which has key products? As there is mismatch with the variable pro and productdetails. As i see pro holds values as "Array of Dictionary with key as String type and Value as Any type" which again has Array above it and productdetails is expecting Array of Any type not the Array of Dictionary Type. Assuming your products has Array of String Type or can be even a class object type you can do it as below.
var pro = Array<Any>() //Or You can make Array<String>() or Array<CustomClass>()
var userdata:[String:[String]] = ["products":["One","Two","Three"]]
var productsdetails = Array<Any>()
productsdetails = userdata["products"] ?? []
print("response\(productsdetails)")
pro.append(productsdetails)
I have to deliver some specific JSON to a webserver, but I'm very confused about how to use the correct arrays and dictionaries, so I can't get to my final result. I'm starting from this:
["entries": [
"member" : [
"id" : key,
"name" : value
],
"timestamp" : "\(dateString)"
]
]
in the JSON file I have to send to the server I can only have 1 "entries" and several "member" with specific "id" and "name" and timestamp. I'm aware that I have to use dictionaries but can't get to this result. This is what I tried so fare:
var leden = [Int: String]()
var jsonDict = [String: Any]()
var dateString = Date()
for (key, value) in leden{
jsonDict["\(key)"] = ["member" : [
"id" : key,
"naam" : value
],
"timestamp" : "\(dateString)"
]
}
jsonFinaal = ["entries": jsonDict]
this is as close as I could get, but good enough because the key in jsonDict should not be there, I added the "entries" in a second dictionary to make this a unique entry.
Anyone who can do beter to help me to the solution?
The value for members is an array of dictionaries, not a dictionary
This creates the structure sorted by the id keys
let leden = [1 : "Foo", 2: "Bar"]
let sortedKeys = leden.keys.sorted()
let dateString = Date()
var members = [[String:Any]]()
for key in sortedKeys {
let naam = leden[key]!
members.append(["member" : ["id" : key, "naam" : naam], "timestamp" : "\(dateString)"])
}
let entries = ["entries": members]
print(entries)
And the JSON string with
do {
let jsonData = try JSONSerialization.data(withJSONObject: entries)
let json = String(data: jsonData, encoding: .utf8)
print(json)
} catch { print(error) }
I need to compare an item in one array of dictionaries to all the items in another array of dictionaries and find out if the first item was in the second array at all.
For example, array1 has a dictionary with [{"name": "zach", "age": "twenty"}, {"name": "ben", "age": "fifteen"}, etc.] in it. Array2 is similar with [{"name" = "eli", "age": "fifteen"}, {"name" = "will", "age": "fifteen"}, etc.].
If zach isn't in array2, I want to add him to it. How can I take the items from the first array of dictionaries and compare them to all the items in the second array so that I'll know whether or not I should add it?
Dictionary doesn't have duplicate key. In your ex you are having name key twice with in the same Dictionary and it is not possible, but if you have array something like below.
let array1 = [["name": "zach"], ["name": "eli"], ["name": "ben"]]
var array2 = [["name": "eli"], ["name" : "will"]]
Then you can try something like to add zach and ben name in array2.
let array2Name = array2.flatMap({$0["name"]}) // ["eli", "will"]
array2 = array1.reduce(array2) { !array2Name.contains($1["name"] ?? "") ? $0 + [$1] : $0 }
print(array2)
Output
[["name": "eli"], ["name": "will"], ["name": "zach"], ["name": "ben"]]
Edit: You are facing this issue because you are having array with type [[String:Any]] and in question you are showing as array of type [[String:String]], so change above code like this will works for you.
let array2Name = array2.flatMap({$0["name"] as? String}) // ["eli", "will"]
array2 = array1.reduce(array2) { !array2Name.contains($1["name"] as? String ?? "") ? $0 + [$1] : $0 }
print(array2)
I'm guessing that your question may be more complicated, but Dictionary is Equatable based on its contents, which means that you can iterate over the first Array, and query the second Array, as to whether it contains the same Dictionary. Like this:
let array1 : [Dictionary<String, String>] = [["one" : "two"], ["three" : "four"]]
let array2 : [Dictionary<String, String>] = [["one" : "two"], ["three" : "five"]]
var doesContain = false
for dict in array1 {
if array2.contains(where: { $0 == dict} ) {
doesContain = true
}
}
print(doesContain) // Should be true in this case, because both arrays contain ["one" : "two"]; if they had no duplicates, doesContain would remain false
If I understood correctly, you want to have a new array of unique dictionaries. This is what I can suggest you:
let array1: [String] = [["name":"test1"], ["name":"test12"]].flatMap { $0["name"] }
let array2: [String] = [["name":"test1"], ["name":"test3"]].flatMap { $0["name"] }
let set = Set<String>(array1).union(Set<String>(array2))
let resultArrayOfDicts = set.map{ ["name": $0] }