-1

In iOS/Swift I've created an indexed "client" UITableView based on the clientName property in my Client class. I created a dictionary with A to Z as the sections. The indexed tableview works great. However, I'm trying to figure out a way to determine which row it is in the original source array, when the user selects a row. I was thinking of building some type of cross reference array, except that the dictionary ends up sorted to match the sections, so I don't know which section/row combo matches which original array entry. Is there a common approach to handling this issue?

In an attempt to clarify...

class Client {
    var clientId             : Int!
    var firstName            : String!
    var lastName             : String!
    var email                : String!
    var phone                : String!
    ...

    init() {

    }
}

var clients: [Client] = []

// clients array loaded from web service
...

// Create dictionary to be source for indexed tableview
func createClientDict() {
    clientDict          = [String: [String]]()
    clientSectionTitles = [String]()

    var clientNames:[String] = []
    for i in 0..<clients.count {
        let client = clients[i]

        let clientName = "\(client.lastName), \(client.firstName)"
        clientNames.append(clientName)
    }

    for name in clientNames {
        var client: Client  = Client()

        // Get the first letter of the name and build the dictionary
        let clientKey = name.substringToIndex(name.startIndex.advancedBy(1))
        if var clientValues = clientDict[clientKey] {
            clientValues.append(name)
            clientDict[clientKey] = clientValues
        } else {
            clientDict[clientKey] = [name]
        }
    }

    // Get the section titles from the dictionary's keys and sort them in ascending order
    clientSectionTitles = [String](clientDict.keys)
    clientSectionTitles = clientSectionTitles.sort { $0 < $1 }
}

Now, when the user taps a row in the tableview, I can get the section and row (indexPath). However, how can I determine which row in the clients array is the match, assuming there could be duplicate names? Is there some way to create a cross reference of indexed section/row mapped to row in source array on the fly? I was going to try to do that while building the dictionary, except that the dictionary gets sorted after, so nothing would match up. Maybe I should somehow be including the source row number in/with the dictionary??

Here is the tableview code:

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCellWithIdentifier("Cell") as! ClientCell

    let clientKey = clientSectionTitles[indexPath.section]
    if let clientValues = clientDict[clientKey] {
        cell.clientName.text = clientValues[indexPath.row]
    }

    return cell
}

func numberOfSectionsInTableView(tableView: UITableView) -> Int {
    return clientSectionTitles.count
}

func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    let clientKey = clientSectionTitles[section]
    if let clientValues = clientDict[clientKey] {
        return clientValues.count
    }

    return 0
}

func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
    return clientSectionTitles[section]
}

func sectionIndexTitlesForTableView(tableView: UITableView) -> [String]? {
    return clientIndexTitles
}

func tableView(tableView: UITableView, sectionForSectionIndexTitle title: String, atIndex index: Int) -> Int {

    guard let index = clientSectionTitles.indexOf(title) else {
        return -1
    }

    return index
}

func tableView(tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
    return 20
}

func tableView(tableView: UITableView, willDisplayHeaderView view: UIView, forSection section: Int) {
    let headerView   = view as! UITableViewHeaderFooterView

    headerView.contentView.backgroundColor = UIColor ( red: 0.0, green: 0.3294, blue: 0.6392, alpha: 1.0 )
    headerView.textLabel?.textColor = UIColor.greenColor()
    headerView.textLabel?.font = UIFont(name: "Noteworthy-bold", size: 15.0)
}

func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
    selectedIndex = indexPath

    // In the following prepare for segue, I need to somehow use the selected indexpath to find the correct entry
    // in the clients array and pass it along.

    performSegueWithIdentifier("clientDetailSegue", sender: self)
}
Lastmboy
  • 1,849
  • 3
  • 21
  • 39
  • This question [needs some help](http://importblogkit.com/2016/03/how-to-ask-a-good-stack-overflow-question/). – nhgrif Mar 19 '16 at 21:16
  • Where is your table view code? Also... this `Client` class is a crash waiting to happen... `!` – nhgrif Mar 19 '16 at 21:41
  • I have edited to add table view code. I've been trying to determine which is more scary in my class definitions. Having all properties as optional has it's own issues, too, but maybe not as bad. – Lastmboy Mar 19 '16 at 21:57
  • Being optionals isn't a problem. Being implicitly unwrapped is a problem. – nhgrif Mar 19 '16 at 21:58
  • ok. Thanks. Still trying to figure out best practices for optionals. – Lastmboy Mar 19 '16 at 22:22

1 Answers1

0

I figured it out. I didn't realize (until I recently tried it) that you could nest an array of any class in a dictionary. When I changed my dictionary to have my clients array nested in it, all was solved. I changed my function as shown below.

func createClientDict() {
    // Declared for view controller. Re-initialized here.
    clientDict          = [String: [Client]]()
    clientSectionTitles = [String]()

    clients.sortInPlace ({ $0.lastName < $1.lastName })

    for c in clients {
        let clientName = "\(c.lastName), \(c.firstName)"

        // Get the first letter of the name and build the dictionary
        let clientKey = clientName!.substringToIndex(clientName!.startIndex.advancedBy(1))
        if var clientValues = clientDict[clientKey] {
            clientValues.append(c)
            clientDict[clientKey] = clientValues
        } else {
            clientDict[clientKey] = [c]
        }
    }

    // Get the section titles from the dictionary's keys and sort them in ascending order
    clientSectionTitles = [String](clientDict.keys)
    clientSectionTitles = clientSectionTitles.sort { $0 < $1 }
}

However, this line was the key to the solution:

let clientDict = [String: [Client]]()
Lastmboy
  • 1,849
  • 3
  • 21
  • 39