Swift 3,排序字典数组

时间:2016-12-30 23:57:14

标签: swift3

我在Swift 3中排序了一系列词典。

在Swift 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) }

将相同的代码转换为Swift 3并不顺利。系统引导我(通过迂回路线):

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

但该应用程序总是崩溃,并且在解开Optional值时发现错误为nil。

将我的头撞在桌子上太长时间后,把所有可能的组合放在一起,我采用了一种旧方法来完成工作:

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

这很有效,而且我一直在前进,但它不是很快,是吗?

一切都出错了?如何在这种情况下使纯Swift排序功能起作用?

3 个答案:

答案 0 :(得分:8)

  

哪里出错了?

你的第一行出了问题:

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

即使在Swift 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"]]

答案 1 :(得分:1)

通常建议您创建自己的自定义类型,而不是使用带有固定密钥集的字典:

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 - 合并运算符,??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方法。

答案 2 :(得分:0)

让描述符:NSSortDescriptor = NSSortDescriptor.init(key:“ YOUR KEY”,升序:true)

让sortedResults:NSArray = tempArray.sortedArray(使用[descriptor])作为NSArray