Append element to existing key in a dictionary? [duplicate] - swift

This question already has answers here:
swift: modifying arrays inside dictionaries
(5 answers)
Closed 7 years ago.
var iniArray = ["yikes", "bye"]
var wordDict: [Int: Array<String>]?
// initialize the wordDict
wordDict[0] = iniArray
Now let's say I want to add another string element to the value of the key 0. For example "bike". So that it becomes:
wordDict[0] = ["yikes", "bye", "bike"]
How can I do this?

It looks like this part of your code:
var wordDict: [Int: Array<String>]?
Is unintentional. Here, you're making the whole dictionary optional, which doesn't make much sense. This is what (I'd imagine) you intended:
var wordDict: [Int: Array<String>]
It's preferred to use the array shorthand, generally, though:
var wordDict: [Int: [String]]
anyway, to initialise this dictionary as an empty dictionary, you need to do this:
var wordDict: [Int: [String]] = [:]
Then, to add the initial array, you do this:
wordDict[0] = ["yikes", "bye"]
And finally, to append the extra element, you use optional chaining:
wordDict[0]?.append("bike")
If you did intend for the entire dictionary to be optional, you just need an extra ?:
var wordDict: [Int: [String]]? = [:]
wordDict?[0] = ["yikes", "bye"]
wordDict?[0]?.append("bike")
wordDict?[0] // ["yikes", "bye", "bike"]

First, it's worth pointing out that there are two problems in your code:
Trying to use wordDict before it is initialized
Trying to use wordDict as if it were not an optional
Because you made wordDict optional, you have to always ensure it is not nil before you use it. Right now, in your code snippet, wordDict is nil.
var iniArray = ["yikes", "bye"]
var wordDict: [Int: Array<String>]?
wordDict[0] = iniArray // error: value of optional type '[Int : Array<String>]?' not unwrapped; did you mean to use '!' or '?'?
The later problem can be solved by using ?:
var iniArray = ["yikes", "bye"]
var wordDict: [Int: Array<String>]?
wordDict?[0] = iniArray
wordDict?[0] = ["yikes", "bye", "bike"]
But if that's the only change you make, then you might be surprised about the value:
print(wordDict) // prints "nil"
This is because you never actually initialized your dictionary before trying to put a value (the array) into it. The syntax wordDict? can be thought of as saying "if wordDict is not nil, then proceed with the rest of the expression". Because it wasn't initialized, the putting of the key into the dictionary never happened. The following code works:
var iniArray = ["yikes", "bye"]
var wordDict: [Int: Array<String>]? = [:] // now `wordDict` is initialized, but still an optional
wordDict?[0] = iniArray
wordDict?[0] = ["yikes", "bye", "bike"]
The second line, were the dictionary is initialized, can be written in several ways, these are all the same:
var wordDict: Dictionary<[String]>? = [:]
var wordDict: Dictionary<Int, [String]>? = [:]
var wordDict: Dictionary<Int, [String]>? = Dictionary()
var wordDict: [Int: [String]]? = [:]
var wordDict: [Int: [String]]? = Dictionary()
In all the above cases, you have initialized an optional, empty dictionary. Any time you try to use that dictionary it needs to be conditionally unwrapped (using ?) or force unwrapped (using !, but it is discouraged to force-unwrap).
Also, if you want to append to the array for that key instead of replacing it completely:
wordDict?[0]?.append("foo")
But these are all value types, so I'm pretty sure this is still replacing the array in wordDict.

You can use the append method on the array class
wordDict.append("bike")
see ref
https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/CollectionTypes.html

Related

Immutable value error when appending to array within dictionary after downcasting

var someDict = [String:Any]()
someDict["foo"] = ["hello"]
(someDict["foo"] as? [String])?.append("goodbye") // error here
I am trying to add a value to an existing dictionary containing an array. The dictionary also contains other non-array values, so it has to have value type Any. The problem is that, when I do this, I get an error Cannot use mutating member on immutable value of type '[String]'. Some Googling turned up a few references such as this suggesting that arrays within dictionaries are always immutable, but the compiler doesn't complain if I do this:
var someDict = [String:[String]]()
someDict["foo"] = ["hello"]
someDict["foo"]?.append("goodbye")
so I suspect that information is outdated and it's something specific to the downcasting. Is there any way I can get around this without copying and re-assigning the entire dictionary value?
Yes, it is related the the downcasting. Try this instead:
var someDict = [String:Any]()
someDict["foo"] = ["hello"]
if var arr = someDict["foo"] as? [String] {
arr.append("goodbye")
someDict["foo"] = arr
}

What's the correct way to create a dictionary typealias?

If I want to define a typealias for a Dictionary, which accept String as the key, and String for the value, but I also want it to can accept nil, which one of these definition is correct?
typealias dictString = Dictionary<String, String?>
or
typealias dictString = Dictionary<String, String>
or
typealias dictString = [String:String?]
or
typealias dictString = [String:String]
Because I use the first one, and now each time I want to unwrap a value, I need to unwrap it twice (e.g. dict["name"]!!) so I began to wondering whether I really need to add the question mark or not (but I need the dictionary to still able to take nil value). Thanks.
The second and fourth are okay.
You don't need to make the value optional in most cases. You can set a key's value to nil even if you did not declare the value as an optional type:
var dict = [String: String]()
dict["Hello"] = nil
And when you access "Hello", it returns nil. Fantastic!
In fact, setting a key's value to nil means to "delete the key". This can cause unexpected results if you don't know this.
For example,
var dict = [String: String]()
dict["Hello"] = nil
print(dict.keys.contains("Hello"))
prints false.
If you need to check if a key exists even if its value is nil, then you have to make the value an optional type.
A dictionary will return nil for any key that's not present. Now, if you want to be able to actually have the key present but have the value nil then version 1 and 3 are the way to go and you keep the double unwrapping. Otherwise use 2 or 4.
Update
Example:
Version 1 & 3:
typealias DictString = Dictionary<String, String?> // or [String: String?]
var myDict: DictString = ["MyKey1": "MyVal1", "MyKey2": nil]
var value1: String? = myDict["MyKey1"]! // This is an Optional String so you'd need to unwrap it
var value2: String? = myDict["MyKey2"]! // This will return nil
Version 2 & 4:
typealias DictString = Dictionary<String, String> // or [String: String]
var myDict: DictString = ["MyKey1": "MyVal1"]
var value1: String? = myDict["MyKey1"] // This is an Optional String so you'd need to unwrap it
var value2: String? = myDict["MyKey2"] // This will return nil
Now the difference between the two is that the first case actually stores the keys. So if you ever do this:
var allKeys: [String] = Array(myDict.keys)
You'll get two different results:
1 & 3: allKeys will be ["MyKey1", "MyKey2"]
2 & 4: allKeys will be ["MyKey1"]
P.S: A good practice is to use upper cases for types' names. So I would suggest to changes your typealias to DictString.

How to declare a dictionary using String as key, and Arrays of Ints as value in Swift language?

How do I declare a dictionary using String as key, and containing Arrays as value in Swift?
This won't work:
var unloadedImagesRows:[String:[Int]()]! = [String:[Int]()]()
You don't need to explicitly declare the type of the variable. Swift is often smart enough to infer the type from its value.
Try this:
var unloadedImagesRows = [String: [Int]]()
unloadedImagesRows["array1"] = [1,2,3,4]
unloadedImagesRows["array2"] = [5,6,7]
This will work:
var unloadedImagesRows:[String:[Int]]! = [String:[Int]]()
var unloadedImagesRows:[String: [Int]] = ["somekey": [1,2,3]]
or for an empty dictionary
var unloadedImagesRows:[String: [Int]] = [ : ]

how to make a swift dictionary that holds two items that are array of other items

I'd like to create a dictionary that holds two arrays (one key is called "locations" and the other is "items") like this:
var tmpResults = Dictionary("locations",Array)
tmpResults["items"]=Array
or something like this (neither of which seem to work):
var tmpResults = ["locations",:<Location>,"items":<Item>]
var tmpResults = ["locations":Array(Location),"items":Array(Item)]
but I'm really not sure how to do this in Swift. How would I specify the types that the arrays could hold?
I think the easiest solution would be AnyObject.
var tmpResults: [String: AnyObject] = [:]
tmpResults["locations"] = [location1, location2, location3]
tmpResults["items"] = [item1, item2, item3]
// Assuming location1, location2, and location3 are all of the same "Location" type
// And item1, item2, and item3 are all of the same "Item" type
By using AnyObject, your dictionaries objects could be almost anything. In this example, you have a dictionary that holds and array of Locations at one key, and an array of Items at another key.
You do lose some of the nice type-checking Swift does though.
Edit
In fact, you could declare it like this, so that the compiler at least knows your dictionary holds arrays:
var tmpResults: [String: [AnyObject]] = [:]
In either case, you use the array you'd probably do something like this:
if let items = tmpResults["items"] as? [Item] {
// Do something with you items here
}
In order to hold array in dictionary, you can go with the following code:
var tempResults : Dictionary<String, Array<String>>
This can be reduced to:
var tempResults : [String: [String]]
Then you can add your Array items to it:
var array1 : [String] = [String]()
array1.append("location1")
array1.append("location2")
array1.append("location3")
var array2 : [String] = [String]()
array2.append("item1")
array2.append("item2")
array2.append("item3")
And finally, you can add it to your Dictionary
tempResults["location"] = array1
tempResults["items"] = array2
Hope this helps!

(String: AnyObject) does not have a member named 'subscript'

I've been through similar questions but still do not understand why my code is throwing an error.
var dict = [String:AnyObject]()
dict["participants"] = ["foo", "bar"]
dict["participants"][0] = "baz"
The error is on line 3: (String: AnyObject) does not have a member named 'subscript'
I'm setting the participants key to an array and then trying to update the first element of it without any luck. The code above is shortened for example purposes, but I am using [String:AnyObject] because it is not only arrays that are stored in the dictionary.
It's probably something really trivial but I am still new to Swift. Thanks for any help in advance!
The error message tells you exactly what the problem is. Your dictionary values are typed as AnyObject. I know you know that this value is a string array, but Swift does not know that; it knows only what you told it, that this is an AnyObject. But AnyObject can't be subscripted (in fact, you can't do much with it at all). If you want to use subscripting, you need to tell Swift that this is not an AnyObject but rather an Array of some sort (here, an array of String).
There is then a second problem, which is that dict["participants"] is not in fact even an AnyObject - it is an Optional wrapping an AnyObject. So you will have to unwrap it and cast it in order to subscript it.
There is then a third problem, which is that you can't mutate an array value inside a dictionary in place. You will have to extract the value, mutate it, and then replace it.
So, your entire code will look like this:
var dict = [String:AnyObject]()
dict["participants"] = ["foo", "bar"]
var arr = dict["participants"] as [String] // unwrap the optional and cast
arr[0] = "baz" // now we can subscript!
dict["participants"] = arr // but now we have to write back into the dict
Extra for experts: If you want to be disgustingly cool and Swifty (and who doesn't??), you can perform the mutation and the assignment in one move by using a define-and-call anonymous function, like this:
var dict = [String:AnyObject]()
dict["participants"] = ["foo", "bar"]
dict["participants"] = {
var arr = dict["participants"] as [String]
arr[0] = "baz"
return arr
}()