繁体   English   中英

在Swift中对具有多个条件的字典数组进行排序

[英]Sorting an Array of Dictionaries with multiple criteria in Swift

我有一组字典,我的字典的类型是[String:AnyObject]。 这是一些数据:

[{
    name = Joe;
    score = 50;
    favorite = Phone;
    startDate = "2015-10-15";
    startingTime = "2015-10-15 19:00:00 +0000";
    finished = "<null>";
}, {
    name = Mark;
    score = 60;
    favorite = Phone;
    startDate = "2015-10-16”;
    startingTime = "2015-10-16 19:00:00 +0000";
    finished = "<null>";
}, {
    name = Joe;
    score = 30;
    favorite = Phone;
    startDate = "2015-10-15";
    startingTime = "2015-10-15 19:00:00 +0000";
    finished = "<null>";
}]

我想要的是能够首先按名称对它们进行排序,然后按startingTime进行排序,然后如果两者都匹配,则按分数排序(不过我将分数存储为字符串)。

我知道这是完全错误的,但我输入了:

test.sort({ $0.name! < $1.name! })

那给了我一个错误“无法使用类型为(((_,_)-> _))的参数列表调用'sort'。”

谁能阐明这种做法?

**更新为具有来自视图控制器的代码以及答案

    import UIKit

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        var allArray = [AnyObject]()
        var array = [String:AnyObject]()

        array["name"] = "Joe"
        array["score"] = 50 as Int
        array["favorite"] = "Phone"
        array["startDate"] = "2015-10-15"
        let date = NSDate()
        array["date"] = date
        array["finished"] = nil
        allArray.append(array)

        array["name"] = "Mark"
        array["score"] = 60 as Int
        array["favorite"] = "Phone"
        array["startDate"] = "2015-10-16"
        array["date"] = date
        array["finished"] = nil
        allArray.append(array)

        array["name"] = "Joe"
        array["score"] = 30 as Int
        array["favorite"] = "Phone"
        array["startDate"] = "2015-10-15"
        array["date"] = date
        array["finished"] = nil
        allArray.append(array)
        println(allArray)

        allArray.sort {
            if let name0 = $0["name"] as? String, name1 = $1["name"] as? String
                where name0 != name1 {
                    return name0 < name1
            }

            // You said 'startingTime' in your question your code showed 'date'
            if let date0 = $0["date"] as? NSDate, date1 = $1["date"] as? NSDate
                where date0.timeIntervalSince1970 != date1.timeIntervalSince1970 {
                    return date0.timeIntervalSince1970 < date1.timeIntervalSince1970
            }

            // You have 'score' as String. I think you would want a numeric type instead
            return ($0["score"] as! Int) < ($1["score"] as! Int)
        }

        println(allArray)

    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }


}

谢谢!

如果编译器像这样奇怪,请尝试使用闭包参数更明确。

这种排序方法似乎对我有用。

arr = arr.sort { a, b in
    if a["name"] == b["name"] {
        if a["startingTime"] == b["startingTime"] {
            return a["score"] > b["score"]
        }
        return a["startingTime"] > b["startingTime"]
    } else {
        return a["name"] > b["name"]
    }
}

看起来像这样工作,后卫只是这样,所以我不必在比较中处理拆包(因为这个原因,我爱后卫,这使代码更清洁IMO)

let test : [[String:AnyObject]] = [["name":"joe", "time":1, "score":5], ["name":"joe", "time":1, "score":3], ["name":"joe", "time":1, "score":7]]

let sorted = test.sort{
    guard let name = $0["name"] as? String,
    let time = $0["time"] as? Int,
    let score = $0["score"] as? Int,
    let name1 = $1["name"] as? String,
    let time1 = $1["time"] as? Int,
    let score1 = $1["score"] as? Int else{
            //unwrapping failed, sort is going to be bad
            return false
    }

    if name == name1 {
        if time == time1 {
            return score < score1
        }else {
            return time < time1
        }
    }else {
        return name < name1
    }

}

print(sorted)

以下是一些代码片段,它们可以使给定的代码更好地工作。 首先将这些添加到文件的开头,以使日期具有可比性。

    public func ==(lhs: NSDate, rhs: NSDate) -> Bool {
        return lhs === rhs || lhs.compare(rhs) == .OrderedSame
    }

    public func <(lhs: NSDate, rhs: NSDate) -> Bool {
        return lhs.compare(rhs) == .OrderedAscending
    }

    extension NSDate: Comparable { }

然后在您的方法中使用下面的语法(这是swift 2):

    var allArray = [AnyObject]()
    var array = [String:AnyObject]()

    array["name"] = "Joe"
    array["score"] = "50"
    array["favorite"] = "Phone"
    array["startDate"] = "2015-10-15"
    let date = NSDate()
    array["startingTime"] = date
    array["finished"] = nil
    allArray.append(array)

    array["name"] = "Mark"
    array["score"] = "60"
    array["favorite"] = "Phone"
    array["startDate"] = "2015-10-16"
    array["startingTime"] = date
    array["finished"] = nil
    allArray.append(array)

    array["name"] = "Joe"
    array["score"] = "30"
    array["favorite"] = "Phone"
    array["startDate"] = "2015-10-15"
    array["startingTime"] = date
    array["finished"] = nil
    allArray.append(array)
    print(allArray)

    allArray.sortInPlace { i,j in (i["name"] as! String) < (j["name"] as! String) || (i["startingTime"] as! NSDate) < (j["startingTime"] as! NSDate) }

如果您使用这样的语法,我确实更喜欢。 这是声明一系列字典的更快捷的方法。

    let xyz = [
        [
            "name" : "Joe",
            "score" : 50,
            "favorite" : "Phone",
            "startDate" : "2015-10-15",
            "startingTime" : "2015-10-15 19:00:00 +0000",
            "finished" : "<null>"
        ], [
            "name" : "Mark",
            "score" : 60,
            "favorite" : "Phone",
            "startDate" : "2015-10-16",
            "startingTime" : "2015-10-16 19:00:00 +0000",
            "finished" : "<null>"
        ], [
            "name" : "Joe",
            "score" : 30,
            "favorite" : "Phone",
            "startDate" : "2015-10-15",
            "startingTime" : "2015-10-15 19:00:00 +0000",
            "finished" : "<null>"
        ]
    ]
    var test = xyz.sort { ($0["name"] as! String) < ($1["name"] as! String) }

只是将我的解决方案添加到已经很拥挤的答案中,因为现有的解决方案都没有一个令我满意。

// allArray is as initiated in your viewDidLoad method

let sortedArray = allArray.sort {
    if let name0 = $0["name"] as? String, name1 = $1["name"] as? String
        where name0 != name1 {
            return name0 < name1
    }

    // You said 'startingTime' in your question but your code showed 'date'
    if let date0 = $0["date"] as? NSDate, date1 = $1["date"] as? NSDate
        where date0.timeIntervalSince1970 != date1.timeIntervalSince1970 {
            return date0.timeIntervalSince1970 < date1.timeIntervalSince1970
    }

    // You have 'score' as String. I think you would want a numeric type instead
    return ($0["score"] as! Int) < ($1["score"] as! Int)
}

由于您使用AnyObject作为值类型,因此代码中充满了强制转换。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM