2016-12-30 99 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方法。