简体   繁体   中英

SwiftUI: Find element position in array

I'm still trying to get my head around Swift/SwiftUI, so apologies for what's likely a really simple question. Consider this simple SwiftUI file:

import SwiftUI

struct sampleStruct {
    let entryID: Int // comes from the system
    let name: String
}


struct TestView: View {
    
    var listEntries = [sampleStruct(entryID: 301, name: "Pete"),
                       sampleStruct(entryID: 5003, name: "Taylor"),
                       sampleStruct(entryID: 13, name: "Suzie")]
    
    var body: some View {
        VStack{
            ForEach(listEntries, id: \.entryID) { idx in
                Text(idx.name)
            }
        }
    }
}

struct TestView_Previews: PreviewProvider {
    static var previews: some View {
        TestView()
    }
}

Here's my question: I'm looking for a simple way to add the position of the element in the array to the text field. Ie instead of Pete, Taylor, Susie, I'd like to get:

1 Pete

2 Taylor

3 Suzie

I thought that this could do the trick:

...
VStack{
            ForEach(listEntries, id: \.entryID) { idx in
                Text( listEntries.firstIndex { $0.id == idx.entryID } )
                Text(idx.name)
            }
        }
...

But, I'm getting the error "No exact matches in call to initializer".

Any help would be appreciated,

Philipp

The simplest approach would be to use enumerated() , which returns a sequence of pairs (index and item):

struct TestView: View {
    
    var listEntries = [sampleStruct(entryID: 301, name: "Pete"),
                       sampleStruct(entryID: 5003, name: "Taylor"),
                       sampleStruct(entryID: 13, name: "Suzie")]
    
    var body: some View {
        VStack{
            ForEach(Array(listEntries.enumerated()), id: \.1.entryID) { (index,item) in
                Text("\(index + 1) \(item.name)")
            }
        }
    }
}

Note that for extremely large collections you might want a different approach for performance reasons, but for anything reasonably sized, this will work well. A quick and easy upgrade to it would be to used indexed() from Swift Algorithms in place of .enumerated() (you could omit the Array(...) wrapper if you did this as well.


If for learning purposes you want to see what you'd have to do to adjust your original approach, you'd want to:

  1. Use .entryID instead of .id , which doesn't exist on your model
  2. Provide a default index in case firstIndex returns nil
  3. Interpolate the result into a String

One solution might look like this:

struct TestView: View {
    
    var listEntries = [sampleStruct(entryID: 301, name: "Pete"),
                       sampleStruct(entryID: 5003, name: "Taylor"),
                       sampleStruct(entryID: 13, name: "Suzie")]
    
    var body: some View {
        VStack{
            ForEach(listEntries, id: \.entryID) { item in
                let index = listEntries.firstIndex { $0.entryID == item.entryID } ?? -1
                Text("\(index + 1) \(item.name)")
            }
        }
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM