I am trying to make a very simple dictionary mapping strings to arrays of strings in Swift. The code looks like the following:
class FirstViewController: UIViewController {
var characters:[String] = []
var adjacency = [String : [String]?]()
override func viewDidLoad() {
super.viewDidLoad()
characters = loadCharacters()
adjacency = loadAdjacency()
var character:String = characters[0]
var adj:[String] = adjacency[character] // This line gives the first compiler error
adj = adjacency["a"] // This line gives the second compiler error
println(adj)
}
func loadCharacters() -> [String] {
return ["a", "b", "c"]
}
func loadAdjacency() -> [String : [String]?] {
return ["a": ["a", "b", "c"], "b": ["b", "c", "d"], "c": ["c", "d", "e"]]
}
}
The first compiler error is:
'String' is not convertible to 'DictionaryIndex<String, [(String)]?>'
The second compiler error is:
'(String, [(String)]?)' is not convertible to '[String]'
As far as I can tell, both of those lines should be equivalent and correct -- I am fetching an array of strings from a dictionary lookup using a string as the key. Where am I going wrong?
If I write it as follows, the code compiles and runs correctly:
class SecondViewController: UIViewController {
var keyArray:[String] = []
var dict = [String : [String]?]()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
keyArray = ["a", "b", "c"]
dict = ["a": ["a", "b", "c"], "b": ["b", "c", "d"], "c": ["c", "d", "e"]]
println(dict[keyArray[1]])
}
}
By the way, I'm using Xcode Version 6.1 (6A1052d), and I'm running OSX 10.9.4. I'm about to upgrade to Yosemite just in case there's something strange happening with my setup, but I imagine I'm just missing something obvious here.
The error message is not very helpful here, it doesn't relate to the actual problem at all....
Solution 1:
var adj/*:[String]??*/ = adjacency[character] // adj is Optional<Optional<[String]>>
adj = adjacency["a"]
println(adj) // Optional(Optional(["a", "b", "c"]))
Solution 2:
var adj/*:[String]*/ = adjacency[character]!! // adj is [String]
adj = adjacency["a"]!!
println(adj) // [a, b, c]
Solution 3 - the safe way:
if let adj = adjacency[character] { // adj is Optional<String[]>
if let adj2 = adj { // adj2 is String[]
println(adj2)
}
}
The Swift Programming Guide states:
Swift’s Dictionary type implements its key-value subscripting as a subscript that takes and receives an optional type. [...] The Dictionary type uses an optional subscript type to model the fact that not every key will have a value, and to give a way to delete a value for a key by assigning a nil value for that key.
For example, in the following code that uses Type Inference, myString is an optional and is nil:
let myDict = ["A" : "Alabama", "D" : "Delaware", "M" : "Montana"]
let myString = myDict["Z"] //dictionary subscripts return optionals (here, it's String?)
You can use Quick Help for Selected Item on myString to check if it's really an optional (Option ⌥ + click or Control ⌃ + Command ⌘ + ?).
Back to your code:
var adj:[String] = adjacency[character]
The previous line can't compile because adjacency[character] returns an optional (that may be nil) but, in the same time, you want adj to be a non optional [String] that can never be nil.
In order to solve this, you can rewrite your code like this:
override func viewDidLoad() {
super.viewDidLoad()
characters = loadCharacters()
adjacency = loadAdjacency()
var character = characters[0] //Quick Helps shows that it's a String
var adj = adjacency[character] //Quick Helps shows that it's a [(String)]??
if let tempAdj = adj { //returns [(String)]?
if let unwrappedAdj = tempAdj { //returns [(String)]
println(unwrappedAdj)
} else {
println("adj is nil")
}
} else {
println("adj is nil")
}
}
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