Assembling a data payload passed to GRMustache.swift for rendering mustache templates, I'm in a scenario where I need to append data to an array previously defined in the dictionary.
My data structure starts off as:
var data: [String: Any] = [
"key1": "example value 1",
"key2": "example value 2",
"items": [
// I need to append here later
]
]
The items key pair is a collection I need to append later within a loop.
To add to the data["items"] array, I'm trying something like:
for index in 1...3 {
let item: [String: Any] = [
"key": "new value"
]
data["items"].append(item)
}
This errors, as value of type Any? has no member append, and binary operator += cannot be applied to operands of type Any? and [String : Any].
This makes sense, as I need to cast the value to append; however, I can't mutate the array.
Casting to array, whether forcing downcast gives the error:
(data["items"] as! Array).append(item)
'Any?' is not convertible to 'Array<_>'; did you mean to use 'as!' to force downcast?
Cannot use mutating member on immutable value of type 'Array<_>'
Seems like my cast is wrong; or, perhaps I'm going about this in the wrong way.
Any recommendation on how to fill data["items"] iteratively over time?
The type of data[Items] isn't Array but actually Array<[String: Any]>.
You could probably squeeze this into fewer steps, but I prefer the clarity of multiple steps:
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
}
If you prefer brevity over clarity, you can perform this operation in a single line, making use of the nil coalescing operator and the + operator for RangeReplaceableCollection (to which Array conforms), the latter used for the "append" step (in fact constructing a new collection which will the replace the existing one when replacing the value of data["items"]).
// example setup
var data: [String: Any] = [
"key1": "example value 1",
"key2": "example value 2",
"items": []
]
// copy-mutate-replace the "items" array inline, adding a new dictionary
data["items"] = (data["items"] as? [[String: Any]] ?? []) + [["key": "new value"]]
print(data)
/* ["key2": "example value 2",
"items": [["key": "new value"]],
"key1": "example value 1"] */
// add another dictionary to the "items" array
data["items"] = (data["items"] as? [[String: Any]] ?? []) + [["key": "new value"]]
print(data)
/* ["key2": "example value 2",
"items": [["key": "new value"], ["key": "new value"]],
"key1": "example value 1"] */
subscript with Dictionary always return optional(?) instance because it may possible that key is not exist in Dictionary. Now you have declare your dictionary as [String:Any] means type of key is String and value is Any.
Now when you write data["item"] it will return you Any? that is not Array so you can not called append and it will return immutable value so you can't directly mutate it.
So you need to type cast it to Array of Dictionary and stored it to instance and append that instance after that replace that object in your dictionary using reassigning it.
for index in 1...3 {
let item: [String: Any] = [
"key": "new value"
]
var array = data["items"] as? [[String:Any]] ?? [[String:Any]]()
array.append(item)
data["items"] = array
}
Now thing is got lot easier if you have Dictionary like [String: [[String:Any]]] for eg.
var data: [String: [[String:Any]]] = [
"items": []
]
for index in 1...3 {
let item: [String: Any] = [
"key": "new value"
]
data["items"]?.append(item)
}
I'm not entirely sure if this works on the [String: Any] type of dictionary but I also highly recommend you create a struct instead of using "Any" for your dictionary. It'll help run things a lot smoother.
On the other hand, I wanted to mention, you can try something like:
yourDictionary["Key", default: []].append(item)
This will initialize an array for that "Key" that will be used in the dictionary if there isn't already an array initialized. Without this, you'd have to check and initialize the array yourself which ends up being some repetitive code.
Hope this helps.
Give you have an Array of items:[String]
He can also add this to the json
var items = ["string1", "Info 2","Text 4"]
data["items"] = items
Related
Swift Xcode version 13.2.1
Here we have two Arrays,(1)var dicSearch=String and (2)var searchingDic: [[String: Any]]=[] I want to assign searchingDic to dicSearch when i implement it than it show error like, Cannot assign value of type '[String]' to type '[[String : Any]]'
here's my code, please anyone help!
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
searchingDic = dicSearch.filter{filtering in
let filterService = filtering["fName"] as? String
return((filterService?.lowercased().contains(searchText.lowercased()))!)
}
It looks like you are trying to create a filtered list based on a search term, but your dicSearch type is an array of strings (i.e: [String]), while your searchingDic is an array of dictionaries (i.e: [[String : Any]]).
This might be confusing when coming from a different language, but in Swift, the following declaration is a dictionary:
var dict: [String: Any] = [
"key1": "value1",
"key2": "value2",
]
so the following:
var arrayOfdicts: [[String: Any]] = [
["foo": "bar"],
["apples": "oranges"],
dict
]
is actually an array, containing a list of dictionaries, notice how I've put the dict declared above in the second array.
The compiler is telling you that you cannot assign a '[String]' to type '[[String : Any]]'
because this:
// example to an array of strings
var fullList: [String] = [
"apples",
"bananas",
"cucumbers"
]
// is not the same as
var arrayOfdicts: [[String: Any]] = [
["foo": "bar"],
["apples": "oranges"],
dict
]
The Array#filter method, iterates the array itself, and returns a new array with only the elements that return true in the return statement.
so either both your arrays need to be [String] or both your arrays need to be [[String:Any]]
example for String arrays:
// array
var fullList: [String] = [
"apples",
"bananas",
"cucumbers"
]
var filteredList: [String] = []
var searchTerm = "b"
filteredList = fullList.filter{ item in
let value = item
return value.lowercased().contains(searchTerm)
}
print(filteredList) // prints ["bananas", "cucumbers"]
an example for filtering with array of dictionaries:
var people: [[String: Any]] = [
["name": "Joe"],
["name": "Sam"],
["name": "Natalie"],
["name": "Eve"]
]
var filteredPeople: [[String: Any]] = []
var nameFilter = "a"
filteredPeople = people.filter{ item in
let value = item["name"] as! String
return value.lowercased().contains(nameFilter)
}
print(filteredPeople) // prints [["name": "Sam"], ["name": "Natalie"]]
Hope this helps :)
I need to recreate the nested dictionary below in code but I am stuck even though I found many questions on the topic.
This is the Dictionary I need to recreate but I am stuck on the "action" string.
This is what I have made
This is my dictionary
var buttonactions: [String:[[String:[String:String]]]] = [:]
and this is how I update the value for testing and "marker" is my class which stores my button actions
marker.buttonactions.updateValue([["Action" : ["array linked of buttons" : "actionKey"]]], forKey: "button actions array")
I am slightly confused how to set up the "action" as a string and "array linked of buttons"
Any help would be great thanks.
I think the dictionary structure should be
var buttonActions : [String: [String: [String:Any]]] = [:]
let array_linked_of_buttons = ["linked button UU":"22308345y1p245", "linked button cat...":"", "linked button":"ATT TRANS"]
let item0Dict: [String:Any] = ["action": "ON_DOWN_SET_UP", "array linked of buttons":array_linked_of_buttons]
let button_actions_array = ["button action array" : item0Dict]
buttonActions.updateValue(button_actions_array, forKey: "button actions")
print(buttonActions)
protocol ButtonActionDictValue {}
extension String: ButtonActionDictValue {}
extension Array: ButtonActionDictValue where Element == [String: String] {}
typealias ButtonAction = [String: ButtonActionDictValue]
let buttonActions: [ButtonAction] = [
[ "action": "ON_DOWN_SET_UP"
, "array linked of buttons": [
[ "linked button UU": "22307"
, "linked button cat": ""
]
]
]
]
I'm kind of new to Swift currently playing around with Stickers.
I have a JSON file with the following structure:
{
"stickers": [{
"filename": "happy_face",
"description": "Happy Face",
"premium": "false",
"categories": ["blue", "green", "red"],
"genders": ["male", "female"]
},{
"filename": "sad_face",
"description": "Sad Face",
"premium": "false",
"categories": ["blue", "green", "red", "yellow"],
"genders": ["male"]
}]
}
Stickers will have the same filename, but will be separated into folders according to their category and gender.
I can read the JSON data just fine. My problem is when I'm trying to make some use of the JSON data.
My goal is to separate the stickers according to their categories, which could vary depending on the sticker, the user will later be able to switch categories, and the correct stickers will be displayed.
let stickerPack = StickerPack.load()
let allStickers = stickerPack!["stickers"] as? [[AnyHashable : Any]]
func getStickersWithCategory(category: String){
var stickers = [AnyObject]()
for sticker in allStickers! {
let cat = sticker["categories"] as? [String]
for item in cat! {
if item.contains(category){
stickers.append(sticker)
}
}
}
}
The result of this is
"Argument type '[AnyHashable : Any]' does not conform to expected type 'AnyObject'"
Can anyone point me in the right direction? Is it possible with this JSON structure? or is it better to have a different structure, with each category and gender separated? this would lead to a lot of repetition. But maybe I'm creating more problems by trying to keep the JSON structure this way.
All help appreciated!
Be more type specific, this avoids that kind of errors.
This JSON array is [[String:Any]] and never AnyHashable because the keys are required to be string
let allStickers = stickerPack!["stickers"] as? [[String:Any]]
stickers is the same type and never something including AnyObject. All JSON types are value types (Any).
var stickers = [[String:Any]]()
You can filter the array swiftier and it's recommended to safely unwrap all optionals
if let stickerPack = StickerPack.load(),
let allStickers = stickerPack["stickers"] as? [[String:Any]] {
stickers = allStickers.filter({ (sticker) -> Bool in
guard let categories = sticker["categories"] as? [String] else { return false }
return categories.contains(category)
})
...
The issue is that you declared allStickers as let allStickers = stickerPack!["stickers"] as? [[AnyHashable : Any]], which means that its type will be [[AnyHashable:Any]]? or Array<Dictionary<AnyHashable:Any>>. When you are iterating through that array in for sticker in allStickers!, the type of sticker will be Dictionary<AnyHashable:Any>. Dictionary is a struct, so it doesn't conform to AnyObject, and hence you cannot add sticker to stickers, which is declared as an array of AnyObjects.
So changing var stickers = [AnyObject]() to var stickers = [Any](), or more specifically var stickers = [[AnyHashable:Any]]() should solve your issue.
func getStickersWithCategory(category: String){
var stickers = [[AnyHashable:Any]]()
for sticker in allStickers! {
let cat = sticker["categories"] as? [String]
for item in cat! {
if item.contains(category){
stickers.append(sticker)
}
}
}
}
Btw you are encouraged to use Codable in Swift 4 when handling JSON.
I am having difficulty constructing this dictionary. My code looks like this:
var array: [String] = []
let params: [String: AnyObject] = [
"presentation": [
"array": array,
"current_index": 0
]
]
The error shows up on the first line "presentation": [ with Contextual type 'AnyObject' cannot be used with dictionary literal. I have tried rewriting the array, initializing the params then setting the values, etc. etc. and I cannot figure out this problem. Any help would be awesome!
Thank you in advance!
Try this in swift 3 it works
var array: [String] = []
let params: [String: AnyObject] = [
"presentation": [
"array": array,
"current_index": 0
] as AnyObject
]
Try this
let params: [String: [String: AnyObject]]
And close the quotes after the current_index key.
if a NSArray data like this:
var arrayData:NSArray = [{name:"aaa",tel:"0000"},{name:"bbb",tel:"0000"}]
how do I filter name = "aaa"
thanks.
In Swift you can use the filter method in Array. The method takes a closure indicating whether to include the value in the new array. For example:
let array: NSArray = ["aaa", "bbb", "ccc"]
let fiteredArray = (array as! Array).filter { $0 != "aaa" }
filteredArray now only contains "bbb" and "ccc".