Is there a way to iterate through a Dictionary
in a ForEach
loop? Xcode says
Generic struct \'ForEach\' requires that \'[Str
Here is how I implemented this:
struct CartView: View {
var cart:[String: Int]
var body: some View {
List {
ForEach(cart.keys.sorted()) { key in
Text(key)
Text("\(cart[key]!)")
}
}
}
}
The first Text View will output the key which is a String. The second Text View will output the value of the Dict at that key which is an Int. The ! that follows this is to unwrap the Optional which contains this Int. In production you would perform checks on this Optional for a more safe way of unwrapping it, but this is a proof of concept.
Xcode: 11.4.1~
...
var testDict: [String: Double] = ["USD:": 10.0, "EUR:": 10.0, "ILS:": 10.0]
...
ForEach(testDict.keys.sorted(), id: \.self) { key in
HStack {
Text(key)
Text("\(testDict[key] ?? 1.0)")
}
}
...
more detail:
final class ViewModel: ObservableObject {
@Published
var abstractCurrencyCount: Double = 10
@Published
var abstractCurrencytIndex: [String: Double] = ["USD:": 10.0, "EUR:": 15.0, "ILS:": 5.0]
}
struct SomeView: View {
@ObservedObject var vm = ViewModel()
var body: some View {
NavigationView {
List {
ForEach(vm.abstractCurrencytIndex.keys.sorted(), id: \.self) { key in
HStack {
Text(String(format: "%.2f", self.vm.abstractCurrencyCount))
Text("Abstract currency in \(key)")
Spacer()
Text(NumberFormatter.localizedString(from: NSNumber(value: self.vm.abstractCurrencytIndex[key] ?? 0.0), number: .currency))
}
}
}
}
}
}
Since it's unordered, the only way is to put it into an array, which is pretty simple. But the order of the array will vary.
struct Test : View {
let dict: [String: Int] = ["test1": 1, "test2": 2, "test3": 3]
var body: some View {
let keys = dict.map{$0.key}
let values = dict.map {$0.value}
return List {
ForEach(keys.indices) {index in
HStack {
Text(keys[index])
Text("\(values[index])")
}
}
}
}
}
#if DEBUG
struct Test_Previews : PreviewProvider {
static var previews: some View {
Test()
}
}
#endif
The syntax errors I encountered using code from some of the answers to this post helped me sort out a solution for my own problem...
Using a dictionary that contained:
key
= a CoreDataEntity
;value
= the count of instances of that entity from a relationship (type NSSet
).I highlight the answer by @J.Doe and comments that an unordered / random collection Dictionary
may not be the best solution to use with table view cells (AppKit NSTableView
/ UIKit UITableView
/ SwiftUI List
rows).
Subsequently, I'll be rebuilding my code to instead work with arrays.
But if you must work with a Dictionary
, here is my working solution:
struct MyView: View {
var body: some View {
// ...code to prepare data for dictionary...
var dictionary: [CoreDataEntity : Int] = [:]
// ...code to create dictionary...
let dictionarySorted = dictionary.sorted(by: { $0.key.name < $1.key.name })
// where .name is the attribute of the CoreDataEntity to sort by
VStack { // or other suitable view builder/s
ForEach(dictionarySorted, id: \.key) { (key, value) in
Text("\(value) \(key.nameSafelyUnwrapped)")
}
}
}
}
extension CoreDataEntity {
var nameSafelyUnwrapped: String {
guard let name = self.name else {
return String() // or another default of your choosing
}
return name
}
}
You can sort your dictionary to get (key, value) tuple array and then use it.
struct ContentView: View {
let dict = ["key1": "value1", "key2": "value2"]
var body: some View {
List {
ForEach(dict.sorted(by: >), id: \.key) { key, value in
Section(header: Text(key)) {
Text(value)
}
}
}
}
}
I was trying to figure this out as well with a Dictionary of enum/Int pairs. I am effectively converting it to array as Andre suggests, but instead of using map I just cast the keys.
enum Fruits : Int, CaseIterable {
case Apple = 0
case Banana = 1
case Strawberry = 2
case Blueberry = 3
}
struct ForEachTest: View {
var FruitBasket : [Fruits: Int] = [Fruits.Apple: 5, Fruits.Banana : 8, Fruits.Blueberry : 20]
var body: some View {
VStack {
ForEach([Fruits](FruitBasket.keys), id:\Fruits.hashValue) { f in
Text(String(describing: f) + ": \(self.FruitBasket[f]!)")
}
}
}
}
struct ForEachTest_Previews: PreviewProvider {
static var previews: some View {
ForEachTest()
}
}