4

Using the given code below I try to iterate over the dictionary wordList which fails with the issue Instance method 'identified(by:)' requires that '(key: Int, value: [String : String])' conform to 'Hashable'.

So my guess is that I have either to apply the protocol Hashable somehow to the Int of the dictionary or there may be another solution which concerns the usage of .identified(by:)

Thank you very much for the help!

struct ContentView: View {
    @State var wordOrder = ["DE", "EN"]

    let wordList: [Int: [String: String]] = [
        0: [
            "DE": "Hallo Welt",
            "EN": "hello world"
        ],
        1: [
            "DE": "Tschüss",
            "EN": "goodbye"
        ],
        2: [
            "DE": "vielleicht",
            "EN": "maybe"
        ]
    ]

    var body: some View {
        Group {
            NavigationView {
                List() {
                    ForEach(wordList.identified(by: \.self)) { wordListEntry in
                        let lang1 = wordListEntry[wordOrder[0]]
                        let lang2 = wordListEntry[wordOrder[1]]
                        WordRow(lang1, lang2)
                    }
                }
                .navigationBarTitle(Text("Alle Wörter"))
            }
        }
    }
}
Daniel Messner
  • 2,305
  • 2
  • 15
  • 15
  • Possible duplicate of [SwiftUI iterating through dictionary with ForEach](https://stackoverflow.com/questions/56675532/swiftui-iterating-through-dictionary-with-foreach) – J. Doe Jun 20 '19 at 15:09
  • Not necessarily. I have checked the other question as well and that shows a viable approach. But that answer suggests creating an array for the keys and an array for the values. I am sure there might be solution for the question presented above which does not require those "sidesteps". – Daniel Messner Jun 20 '19 at 15:11
  • But how do you overcome the fact that a `Dictionary` is unordered, yet you are trying to catch when the array is reordered? If all `Hashable` does is "identify" the unique array element, you already have it with the Dictionary key! No matter what, you'll need *at least* one array for the `List()` order. –  Jun 20 '19 at 15:39

2 Answers2

10

It looks like you have a misunderstanding. Based on the code you posted, I guess you think that iterating over a dictionary iterates over the values in the dictionary. But that is not how dictionary iteration works.

When you iterate over a dictionary, you receive pairs. Each pair contains one key and the corresponding value from the dictionary. In your code, wordListEntry's type is (key: Int, value: [String: String]), a pair whose first element is key of type Int and whose second element is value of type [String: String].

I think you want to just iterate over the dictionary's keys, and then look up the corresponding values inside the ForEach body, like this:

ForEach(wordList.keys.sorted().identified(by: \.self)) { key in
    let lang1 = wordListEntry[wordOrder[0]]
    let lang2 = wordListEntry[wordOrder[1]]
    return WordRow(lang1, lang2)
}
rob mayoff
  • 375,296
  • 67
  • 796
  • 848
  • Thanks. This is the way to go :) – Daniel Messner Jun 20 '19 at 16:31
  • A little further question: I noticed that you removed the variables inside the ForEach loop. My code also fails due to this because it seems that is not possible to use a var inside. Is there a workaround? – Daniel Messner Jun 20 '19 at 16:41
  • I thought `ForEach` used `@ViewBuilder`, which (as of Xcode 11 beta 2) doesn't allow local variables. However, I was wrong. But since the closure is not a single expression, you have to explicitly use the `return` statement. I have updated my answer. – rob mayoff Jun 20 '19 at 16:53
  • Oh, thats a serious limitation right now. Hopefully this will be improved soon. Thank you for updating your answer! – Daniel Messner Jun 20 '19 at 16:54
  • Can you pls help me out on this https://stackoverflow.com/questions/56814962/map-value-in-particular-key-in-array-of-dictionary – karthikeyan Jun 29 '19 at 06:48
0

Updated for latest swift UI:

    ForEach(wordList.keys.sorted(), id: \.self) { key in
    let lang1 = wordListEntry[wordOrder[0]]
    let lang2 = wordListEntry[wordOrder[1]]
    return WordRow(lang1, lang2)
}
Zack117
  • 985
  • 1
  • 13
  • 28