2016-12-30 98 views
0

我在Swift 3中排序字典數組。Swift 3,排序字典數組

在斯威夫特2,我會做這種方式,它工作得很好:

var dicArray = [Dictionary<String, String>()] 
let dic1 = ["last": "Smith", "first": "Robert"] 
dicArray.append(dic1) 
let dic2 = ["last": "Adams", "first": "Bill"] 
dicArray.append(dic2) 
let sortedArray = dicArray.sort { ($0["last"] as? String) < ($1["last"] as? String) } 

轉換相同的代碼來斯威夫特3已經並不順利。引導我到這(通過迂迴路線)系統:

let sortedArray = dicArray.sorted { ($0["last"]! as String) < ($1["last"]! as String) } 

但應用老是死機,並出現錯誤,它發現零而展開的可選值。 ?!

敲我的頭靠在桌子太久,把S和S在每一個可以想象的組合後,我使出老辦法來完成這項工作:

let sortedArray = (dicArray as NSArray).sortedArray(using: [NSSortDescriptor(key: "last", ascending: true)]) as! [[String:AnyObject]] 

這工作,我一起前進,但它不是非常Swifty,是嗎?

這一切都出錯了?我怎樣才能使純Swift排序功能在這樣的情況下工作?

+2

如果你有一組靜態鍵,你肯定應該使用一個結構而不是字典。 – Alexander

+2

正如@亞歷山大寫道的,使用一個結構:如果關鍵字不是靜態的,上面的排序是非常脆弱的w.r.t.運行時安全性,並且應該驗證字典數組中的所有字典確實包含關鍵「最後」,例如,檢查'dicArray.contains(其中:{!$ 0.keys.contains(「last」)})'不是'true'。如果你首先加入了這樣一名警衛,那麼你會發現空的額外字典,因爲這會違反輸入驗證。 – dfri

回答

5

這一切都出錯了?

出了問題你的第一行:

var dicArray = [Dictionary<String, String>()] 

從來沒有你想要的東西,即使是在斯威夫特2,因爲你實際插入一個額外的,空的字典入陣。這就是崩潰的來源;空字典沒有"last"鍵,因爲它是空的。

你想這樣的:

var dicArray = [Dictionary<String, String>]() 

看到區別?這種變化之後,一切都屬於地方:

var dicArray = [Dictionary<String, String>]() 
let dic1 = ["last": "Smith", "first": "Robert"] 
dicArray.append(dic1) 
let dic2 = ["last": "Adams", "first": "Bill"] 
dicArray.append(dic2) 
let sortedArray = dicArray.sorted {$0["last"]! < $1["last"]!} 
// [["first": "Bill", "last": "Adams"], ["first": "Robert", "last": "Smith"]] 
+0

我不敢相信我做到了。好眼睛。非常感謝。 – Retro

0

而不是使用字典有固定的一套鑰匙,這是一般建議創建自己的自定義類型:

struct Person { 
    let lastName: String 
    let firstName: String 
} 

這樣,你永遠不會有擔心你是否在字典中獲得特定值的關鍵字,因爲編譯器會強制檢查屬性的名稱。它使編寫健壯,無錯的代碼更容易。

而且,巧合的是,它也使分類更清潔。爲了讓這個自定義類型排序,你讓它符合Comparable協議:

extension Person: Comparable { 
    public static func ==(lhs: Person, rhs: Person) -> Bool { 
     return lhs.lastName == rhs.lastName && lhs.firstName == rhs.firstName 
    } 

    public static func < (lhs: Person, rhs: Person) -> Bool { 
     // if lastnames are the same, compare first names, 
     // otherwise we're comparing last names 

     if lhs.lastName == rhs.lastName { 
      return lhs.firstName < rhs.firstName 
     } else { 
      return lhs.lastName < rhs.lastName 
     } 
    } 
} 

現在,你可以對它們進行排序,保持很好地封裝在Person類型中比較邏輯:

let people = [Person(lastName: "Smith", firstName: "Robert"), Person(lastName: "Adams", firstName: "Bill")] 
let sortedPeople = people.sorted() 

現在,無可否認,上述閃避了你如何比較可選項的隱含問題。因此,以下是firstNamelastName爲可選項的示例。但是,而不是擔心在哪裏放置?!,我會使用nil -coalescing操作,??,或者switch語句,如:

struct Person { 
    let lastName: String? 
    let firstName: String? 
} 

extension Person: Comparable { 
    public static func ==(lhs: Person, rhs: Person) -> Bool { 
     return lhs.lastName == rhs.lastName && lhs.firstName == rhs.firstName 
    } 

    public static func < (lhs: Person, rhs: Person) -> Bool { 
     // if lastnames are the same, compare first names, 
     // otherwise we're comparing last names 

     var lhsString: String? 
     var rhsString: String? 
     if lhs.lastName == rhs.lastName { 
      lhsString = lhs.firstName 
      rhsString = rhs.firstName 
     } else { 
      lhsString = lhs.lastName 
      rhsString = rhs.lastName 
     } 

     // now compare two optional strings 

     return (lhsString ?? "") < (rhsString ?? "") 

     // or you could do 
     // 
     // switch (lhsString, rhsString) { 
     // case (nil, nil): return false 
     // case (nil, _): return true 
     // case (_, nil): return false 
     // default: return lhsString! < rhsString! 
     // } 
    } 
} 

switch說法更加明確關於nil處理值(例如nil在非可選值之前或之後進行排序),並且會區分nil值和空字符串,如果需要的話。 nil合併運算符更簡單(對於最終用戶而言,恕我直言,更直觀),但如果需要,可以使用switch方法。