I store strings of a view controller in a string array. I import this string array as a Data Source in my table view. This all works smoothly. But now I would like to sort t
I would change the way you store your contacts to a dictonary with the initial letters as keys and put the names that correspond to that initial letter into a subarray:
contacts = ["A": ["Anton", "Anna"], "C": ["Caesar"]]
I simplified the way of the contacts here (in form of strings), but you get the concept.
I would also save the section number of the letter in a seperate array like this:
letters = ["A", "C"]
Keep the array sorted and organized, so check after each insertion/deletion/update. This is not part of the table view implementation. I would make the Viewcontroller a delegate of the phonebook, so you can fire an update-like method from the phonebook to update the table.
How to get the data for the data source:
the number of sections:
letters.count
the section title for section at index i is
letters[i]
the number of cells in a section i is
contacts[letters[i]].count
and the content for a specific cell c in section i is:
contacts[letters[i]][c]
Feel free to ask further questions if anything is still not clear.
UPDATE - How to generate the arrays:
I don't require the data to be sorted, if you pass it already sorted, you can delete the sorting lines below ...
let data = ["Anton", "Anna", "John", "Caesar"] // Example data, use your phonebook data here.
// Build letters array:
var letters: [Character]
letters = data.map { (name) -> Character in
return name[name.startIndex]
}
letters = letters.sort()
letters = letters.reduce([], combine: { (list, name) -> [Character] in
if !list.contains(name) {
return list + [name]
}
return list
})
// Build contacts array:
var contacts = [Character: [String]]()
for entry in data {
if contacts[entry[entry.startIndex]] == nil {
contacts[entry[entry.startIndex]] = [String]()
}
contacts[entry[entry.startIndex]]!.append(entry)
}
for (letter, list) in contacts {
list.sort()
}
For Swift 3:
let data = ["Anton", "Anna", "John", "Caesar"] // Example data, use your phonebook data here.
// Build letters array:
var letters: [Character]
letters = data.map { (name) -> Character in
return name[name.startIndex]
}
letters = letters.sorted()
letters = letters.reduce([], { (list, name) -> [Character] in
if !list.contains(name) {
return list + [name]
}
return list
})
// Build contacts array:
var contacts = [Character: [String]]()
for entry in data {
if contacts[entry[entry.startIndex]] == nil {
contacts[entry[entry.startIndex]] = [String]()
}
contacts[entry[entry.startIndex]]!.append(entry)
}
for (letter, list) in contacts {
contacts[letter] = list.sorted()
}
I ran the code in playground and got the following outputs for
letters:
["A", "C", "J"]
contacts:
["J": ["John"], "C": ["Caesar"], "A": ["Anton", "Anna"]]
I did it within one loop, not few (Swift 4):
struct ContactData {
let longName: String
let phones: [String]
let thumbnailImageData: Data?
}
var contacts = [ContactData]()
var tableViewSource = [Character : [ContactData]]()
var headerTitles = [Character]()
func createContactsData(completionHandler: @escaping () -> Swift.Void) {
contacts = extractContacts() // convert CNContact to custom ContactData
tableViewSource.removeAll()
var prevChar: Character?
var currentBatch: [ContactData]!
contacts.forEach { contact in
guard let firstChar = contact.longName.first else {
return
}
if prevChar != firstChar {
if prevChar != nil {
tableViewSource[prevChar!] = currentBatch
}
prevChar = firstChar
currentBatch = [ContactData]()
}
currentBatch.append(contact)
}
let allKeys = Array(tableViewSource.keys)
let sortedSymbols = allKeys.sorted(by: {$0 < $1})
headerTitles = sortedSymbols
completionHandler()
}
For Swift 3. Thank you @Stefan! Here is my version with Set
var tableViewSource: [Character : [String]]!
var tableViewHeaders: [Character]!
let data = ["Anton", "Anna", "John", "Caesar"]
func createTableData(wordList: [String]) -> (firstSymbols: [Character], source: [Character : [String]]) {
// Build Character Set
var firstSymbols = Set<Character>()
func getFirstSymbol(word: String) -> Character {
return word[word.startIndex]
}
wordList.forEach {_ = firstSymbols.insert(getFirstSymbol(word: $0)) }
// Build tableSourse array
var tableViewSourse = [Character : [String]]()
for symbol in firstSymbols {
var words = [String]()
for word in wordList {
if symbol == getFirstSymbol(word: word) {
words.append(word)
}
}
tableViewSourse[symbol] = words.sorted(by: {$0 < $1})
}
let sortedSymbols = firstSymbols.sorted(by: {$0 < $1})
return (sortedSymbols, tableViewSourse)
}
func getTableData(words: [String]) {
tableViewSource = createTableData(wordList: words).source
tableViewHeaders = createTableData(wordList: words).firstSymbols
}
getTableData(words: data)
print(tableViewSource) // ["J": ["John"], "C": ["Caesar"], "A": ["Anna", "Anton"]]
print(tableViewHeaders) // ["A", "C", "J"]