2016-02-11 39 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