Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do you sort data in a tableView alphabetically by section using a custom model class?

I'm new to coding and have been trying to sort the data (names) in my tableview rows alphabetically by section. I've managed to create the sections and an index, but can't figure out how to get the names to sort; every section has the same list of names from A-Z.

I have a model class of Names that has objects: nameTitle, nameDetail, image, latitude coordinates and longitude coordinates. The tableView displays nameTitle.text. The rest of the objects are displayed or accessed in another view controller.

Here is sample code from my TableViewController:

import UIKit

class TableOfContentsVC: UIViewController, UITableViewDelegate,  UITableViewDataSource {

@IBAction func backButtonPressed(_ sender: Any) {

    dismiss(animated: false, completion: nil)

}

@IBOutlet weak var tableView: UITableView?

var name = [Name]()

override func viewDidLoad() {
    super.viewDidLoad()

    let cell001 = Name (nameTitle: "Acker" , nameDetail: "Details for Acker are listed here.", picture: UIImage (named: "somePicture.jpg"), latCoordinates: 38, longCoordinates: 119)

    name.append (cell001)

    let cell002 = Name (nameTitle: "Baker" , nameDetail: "Details for Baker are listed here.", picture: UIImage (named: "somePicture.jpg"), latCoordinates: 38, longCoordinates: 119)

    name.append (cell002)

    let cell003 = Name (nameTitle: "Caker" , nameDetail: "Details for Caker are listed here.", picture: UIImage (named: "somePicture.jpg"), latCoordinates: 38, longCoordinates: 119)

    name.append (cell003)

    let cell004 = Name (nameTitle: "Dacker" , nameDetail: "Details for Dacker are listed here.", picture: UIImage (named: "somePicture.jpg"), latCoordinates: 38, longCoordinates: 119)

    name.append (cell004)

    let cell005 = Name (nameTitle: "Ecker" , nameDetail: "Details for Ecker are listed here.", picture: UIImage (named: "somePicture.jpg"), latCoordinates: 38, longCoordinates: 119)

    name.append (cell005)

    let cell006 = Name (nameTitle: "Facker" , nameDetail: "Details for Facker are listed here.", picture: UIImage (named: "somePicture.jpg"), latCoordinates: 38, longCoordinates: 119)

    name.append (cell006)
}
func tableView(_ tableView: UITableView, sectionForSectionIndexTitle title: String, at index: Int) -> Int {
    let currentCollation = UILocalizedIndexedCollation.current() as UILocalizedIndexedCollation
    return currentCollation.section(forSectionIndexTitle: index)
}

func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
    let currentCollation = UILocalizedIndexedCollation.current() as UILocalizedIndexedCollation
    let sectionTitles = currentCollation.sectionTitles as NSArray
    return sectionTitles.object(at: section) as? String

}

func sectionIndexTitles(for tableView: UITableView) -> [String]? {
    let currentCollation = UILocalizedIndexedCollation.current() as UILocalizedIndexedCollation
    return (currentCollation.sectionIndexTitles as NSArray) as? [String]


}

func numberOfSections(in tableView: UITableView) -> Int {
    let currentCollation = UILocalizedIndexedCollation.current() as UILocalizedIndexedCollation
    let sectionTitles = currentCollation.sectionTitles as NSArray
    return sectionTitles.count

}


func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

    if let cell = tableView.dequeueReusableCell(withIdentifier: "NameCell", for: indexPath) as? NameCell {

        let name = Name [indexPath.row]

        cell.updateUI(name: name)

    return cell


    } else {

        return UITableViewCell()

    }
}


func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {

            return name.count


}

func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {

    let name = names [indexPath.row]
    performSegue(withIdentifier: "ContentVC", sender: name)

}

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {

    if let destination = segue.destination as? ContentVC {

        if let name = sender as? Name {
            destination.name = name
        }
    }
}  
}

Everything works great except for getting the names to show up in the respective section alphabetically. I've tried about 10 different ways to get this to work and nothing has helped. Any suggestions would be greatly appreciated. Thanks in advance!

like image 777
dalex Avatar asked Dec 19 '22 10:12

dalex


1 Answers

You are doing it the wrong way. You are not using your data to generate your index.

Let's consider you have an array of your data var name: [Name]

  1. Define the section into which every Name belongs:
extension Name {
    var titleFirstLetter: String {
        return String(self.nameTitle[self.nameTitle.startIndex]).uppercased()
    }
}
  1. Generate the index from your data
// all the first letters in your data
let firstLetters = names.map { $0.titleFirstLetter }
// some letters appear multiple times, let's remove duplicates
let uniqueFirstLetters = Array(Set(firstLetters))
// sort them
// this is your index
let sortedFirstLetters = uniqueFirstLetters.sorted()
  1. Generate sections
let sections: [[Name]] = sortedFirstLetters.map { firstLetter in
    return names
        .filter { $0.titleFirstLetter == firstLetter } // only names with the same first letter in title
        .sorted { $0.nameTitle < $1.nameTitle } // sort them
}
  1. Use them
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
    return sortedFirstLetters[section]
}

func sectionIndexTitles(for tableView: UITableView) -> [String]? {
    return sortedFirstLetters
}

func numberOfSections(in tableView: UITableView) -> Int {
    return sections.count // or sortedFirstLetters.count
}

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return sections[section].count
}

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let name = sections[indexPath.section][indexPath.row]
    ...
}

Edit - complete example:

class Name {
    let nameTitle: String
    let nameDetail: String

    init(nameTitle: String, nameDetail: String) {
        self.nameTitle = nameTitle
        self.nameDetail = nameDetail
    }

    var titleFirstLetter: String {
        return String(self.nameTitle[self.nameTitle.startIndex]).uppercased()
    }
}

class ViewController : UIViewController, UITableViewDelegate, UITableViewDataSource {
    @IBOutlet weak var tableView: UITableView?

    var names: [Name] = []

    var sortedFirstLetters: [String] = []
    var sections: [[Name]] = [[]]

    override func viewDidLoad() {
        super.viewDidLoad()

        let cell001 = Name(nameTitle: "Acker", nameDetail: "Details for Acker are listed here.")
        names.append (cell001)

        let cell002 = Name(nameTitle: "Baker", nameDetail: "Details for Baker are listed here.")
        names.append (cell002)

        let cell003 = Name(nameTitle: "Caker" , nameDetail: "Details for Caker are listed here.")
        names.append (cell003)

        let cell004 = Name(nameTitle: "Dacker", nameDetail: "Details for Dacker are listed here.")
        names.append (cell004)

        let cell005 = Name(nameTitle: "Ecker", nameDetail: "Details for Ecker are listed here.")
        names.append (cell005)

        let cell006 = Name(nameTitle: "Facker", nameDetail: "Details for Facker are listed here.")
        names.append (cell006)

        let firstLetters = names.map { $0.titleFirstLetter }
        let uniqueFirstLetters = Array(Set(firstLetters))

        sortedFirstLetters = uniqueFirstLetters.sorted()
        sections = sortedFirstLetters.map { firstLetter in
            return names
                .filter { $0.titleFirstLetter == firstLetter }
                .sorted { $0.nameTitle < $1.nameTitle }
        }
    }

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

    func sectionIndexTitles(for tableView: UITableView) -> [String]? {
        return sortedFirstLetters
    }

    func numberOfSections(in tableView: UITableView) -> Int {
        return sections.count
    }

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return sections[section].count
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let name = sections[indexPath.section][indexPath.row]

        let cell = UITableViewCell(style: .subtitle, reuseIdentifier: nil)
        cell.textLabel?.text = name.nameTitle
        cell.detailTextLabel?.text = name.nameDetail

        return cell
    }
}

example screenshot

like image 129
Sulthan Avatar answered Jan 11 '23 23:01

Sulthan