2016-02-11 40 views
0

我已经编写了一个算法来为tableview创建分区索引。 不幸的是,当列表只包含一个结果为空的项目时,我有一个错误。Swift:为名称列表创建分区索引

你有一个优雅的解决方案吗?

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

这里有一个在线解决方案打造的部分名单:

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部分的数量作出回应,但也将使其更容易操纵每个部分内索引路径和数据:

  • 在一节参加人数:章节[指数] .participants.count
  • 参与者在索引路径:章节[indexPath.section]。参与者[indexPath.row]

这仅仅是信达ctic糖果,但如果你有很多对参与者列表的引用,它会使代码更具可读性。另外,如果您的参与者是对象而不是元组或结构体,那么甚至可以更新主参与人列表中的数据,而无需重新构建这些部分(除非姓氏已更改)。

[编辑]固定在上元组的语法错误

[EDIT2]斯威夫特4 ...

斯威夫特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} 
+0

非常感谢你为你的伟大的答案。我试图整合你的第二个片段,并得到“匿名关闭参数不能在具有明确参数的闭包内部使用”的行标题:$ 0, – netshark1000

+0

我忘了用字母替换$ 0,当我为了清晰(重写)而重写它时。现在修复它。 –

+0

这不适用于Swift 3。 !参与者[$ 0 - 1]不是所有的 – netshark1000