我编写了一个算法来为tableview创建一个段索引。 不幸的是,当列表只包含一个结果为空的项时,我有一个错误。
你有一个优雅的解决方案吗?
var sections : [(index: Int, length :Int, title: String)] = Array()
func createSectionIndices(participants: List<Participant>){
sections.removeAll()
var index = 0;
let array = participants.sort({$0.lastName < $1.lastName})
for i in 0.stride(to: array.count, by: 1){
let commonPrefix = array[i].lastName.commonPrefixWithString(array[index].lastName, options: .CaseInsensitiveSearch)
if (commonPrefix.isEmpty) {
let string = array[index].lastName.uppercaseString;
let firstCharacter = string[string.startIndex]
let title = "\(firstCharacter)"
let newSection = (index: index, length: i - index, title: title)
sections.append(newSection)
index = i;
}
}
print("sectionCount: \(sections.count)")
}
答案 0 :(得分:0)
这是构建部分列表的一行解决方案:
var participants:[(firstName:String, lastName:String)] =
[
("John", "Smith"),
("Paul", "smith"),
("Jane", "Doe"),
("John", "denver"),
("Leo", "Twain"),
("Jude", "law")
]
// case insensitive sort (will keep "denver" and "Doe" together)
participants = participants.sort({$0.lastName.uppercaseString < $1.lastName.uppercaseString})
// The process:
// - get first letter of each name (in uppercase)
// - combine with indices (enumerate)
// - only keep first occurrence of each letter (with corresponding indice)
// - build section tuples using index, letter and number of participants with name begining with letter
let sections = participants
.map({String($0.lastName.uppercaseString.characters.first!)})
.enumerate()
.filter({ $0 == 0 || !participants[$0 - 1].lastName.uppercaseString.hasPrefix($1) })
.map({ (start,letter) in return
(
index: start,
length: participants.filter({$0.lastName.uppercaseString.hasPrefix(letter)}).count,
title: letter
)
})
// sections will contain:
// (index:0, length:2, title:"D")
// (index:2, length:1, title:"L")
// (index:3, length:2, title:"S")
// (index:5, length:1, title:"T")
您可能已经有很多现有代码基于存储在元组数组中的部分,但如果没有,我建议您稍微改变一下,并使用字母和参与者数据构建您的sections数组。
let sections = participants
.map({ String($0.lastName.uppercaseString.characters.first!) })
.reduce( Array<String>(), combine: { $0.contains($1) ? $0 : $0 + [$1] })
.map({ (letter) in return
(
title: letter,
participants: participants.filter({$0.lastName.uppercaseString.hasPrefix(letter)})
)
})
这将允许您使用sections.count响应部分的数量,但也可以更容易地操作每个部分中的索引路径和数据:
这只是语法糖果,但如果您对参与者列表有很多引用,它将使代码更具可读性。
此外,如果您的参与者是对象而不是元组或结构,您甚至可以更新主参与者列表中的数据,而无需重建部分(除非更改了姓氏)。
[EDIT]修复了最后一个元组语法中的错误
[EDIT2] Swift 4 ......
Swift 4中的词典提供了一种更简单的方法来管理这类事情。
用于原始参考结构:
let sections = [Character:[Int]](grouping:participants.indices)
{participants[$0].lastName.uppercased().first!}
.map{(index:$1.reduce(participants.count,min), length:$1.count, title:String($0))}
.sorted{$0.title<$1.title}
以及包含自己的参与者子列表的部分结构(我的建议):
let sectionData = [Character:[Participant]](grouping:participants)
{$0.lastName.uppercased().first!}
.map{(title:$0, participants:$1)}
.sorted{$0.title<$1.title}