我已经编写了一种算法来为表视图创建节索引。
不幸的是,当列表仅包含一项时,我有一个错误,结果为空。
您对此有一个优雅的解决方案吗?
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)")
}
最佳答案
这是构建部分列表的单行解决方案:
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")
基于存储在元组数组中的节,您可能已经有很多现有的代码,但是,如果没有,我建议您采取一些不同的方法,并用字母和参与者数据构建您的节数组。
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来响应节的数量,但也将使在每个节中操作索引路径和数据变得更加容易:
一个部分的参与者数量:sections [index] .participants.count
索引路径上的参与者:section [indexPath.section] .participants [indexPath.row]
这只是语法糖果,但是如果您对参与者列表有很多引用,它将使代码更具可读性。
另外,如果您的参与者是对象而不是元组或结构,则您甚至可以更新主要参与者列表中的数据,而不必重建这些部分(除非更改了姓氏)。
[编辑]修复了最后一个元组语法中的错误
[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}