Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

'String' is not convertible to 'DictionaryIndex<String, [(String)]?>' compiler error with very basic dictionary usage

Tags:

swift

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.

like image 909
itfische Avatar asked Sep 02 '26 02:09

itfische


2 Answers

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)
    }
}
like image 105
Bryan Chen Avatar answered Sep 05 '26 15:09

Bryan Chen


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")
    }

}
like image 21
Imanou Petit Avatar answered Sep 05 '26 15:09

Imanou Petit



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!