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?
A Dictionary object contains a set of key-item pairs. A dictionary's item is a value that is unambiguously associated with a unique key and the key is used to retrieve the item. The key can be of any type except a variant or an array (but generally it is a string or still an integer).
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.
Arrays are ordered collections of values. Sets are unordered collections of unique values. Dictionaries are unordered collections of key-value associations. Arrays, sets, and dictionaries in Swift are always clear about the types of values and keys that they can store.
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"] */
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With