Array Won't Sort Correctly : Swift 4

2019-06-14 08:55发布

问题:

I was trying to sort an array of custom objects but for some reason, the code that worked a few weeks back won't work anymore. It is supposed to check if $0 and $1 have the same date, and if they do, it is supposed to sort the array by id, but currently it won't sort correctly and when I print the array, I get the following output:

11-07-2017 : 1
11-07-2017 : 10
11-07-2017 : 11
11-07-2017 : 12
11-07-2017 : 13
11-07-2017 : 14
11-07-2017 : 15
11-07-2017 : 16
11-07-2017 : 17
11-07-2017 : 18
11-07-2017 : 19
11-07-2017 : 2
11-07-2017 : 20
11-07-2017 : 3
11-07-2017 : 4
11-07-2017 : 5
11-07-2017 : 7
11-07-2017 : 8
11-07-2017 : 9
11-08-2017 : 1
11-08-2017 : 2
11-09-2017 : 1
11-09-2017 : 2

As can be seen above, the dates that only have a few entries sort correctly, but the dates with more entries (11-07-17) don't.

Below is my code:

Model for the Array:

struct BalanceUser {

    var id = ""

    var name = ""

    var date = ""

}

Current Sorting Code:

self.sortedTableArray.sort(by: {
       if $0.date != $1.date {
             return $0.date < $1.date
       } else {
             return $0.id < $1.id
       }
})

Firebase Code (As Requested):

ref.child("Admin").child("Balances").observeSingleEvent(of: .value, with: { (snapshot) in

        let value = snapshot.value as? NSDictionary

        if value!.count > 1 {

            let specificValues = value?.allKeys

            for balanceUser in specificValues! {

                var user = BalanceUser()

                user.date = balanceUser as! String

                if balanceUser as? String != "balance" {

                    var i = 0

                    var counter = 0

                    while i < 101 {

                        self.ref.child("Admin")
                            .child("Balances")
                            .child(balanceUser as! String)
                            .child(String(i))
                            .observeSingleEvent(of: .value, with: { (snapshot) in

                                let nameValue = snapshot.value as? NSDictionary

                                if nameValue != nil {

                                    user.id = counter

                                    var j = 0

                                    while j < (nameValue?.count)! {

                                        let item = nameValue?.allKeys[j] as? String

                                        var aItem = ""

                                        if let item = nameValue?.allValues[j] as? String {

                                            aItem = item

                                        } else if let item = nameValue?.allValues[j] as? NSNumber {

                                            aItem = String(describing: item)

                                        } else if let item = nameValue?.allValues[j] as? Int {

                                            aItem = String(describing: item)

                                        }


                                        if item == "name" {

                                            user.name = aItem

                                        } else if item == "money" {

                                            user.money = aItem

                                        } else if item == "balance" {

                                            user.balance = aItem

                                        } else if item == "status" {

                                            user.status = aItem

                                        }

                                        j += 1

                                    }


                                    let dateFormatter = DateFormatter()

                                    dateFormatter.dateFormat = "MM-dd-yyyy"

                                    if user.date.components(separatedBy: "-")[0] == dateFormatter.string(from: Date()).components(separatedBy: "-")[0] {

                                        self.sortedTableArray.append(user)

                                        self.sortedTableArray.sort(by: { (object1, object2) -> Bool in

                                            if object1.date == object2.date {

                                                return object1.id < object2.id

                                            } else {

                                                return object1.date < object2.date

                                            }

                                        })

                                    }

                                    self.tableArray.append(user)

                                    self.tableArray.sort(by: { (object1, object2) -> Bool in

                                        if object1.date == object2.date && object1.year == object2.year {

                                            return object2.id > object1.id

                                        } else {

                                            return object1.date < object2.date || object1.year < object2.year

                                        }

                                    })

                                    self.tableView.reloadData()

                                }

                                counter += 1

                            }) { (error) in

                                print(error.localizedDescription)

                        }

                        i += 1

                    }

                }

            }

        } else {

            self.view.makeToast(message: "No Users Found in Database")

        }

    }) { (error) in

        print(error.localizedDescription)

    }

回答1:

It's sorting absolutely correct as you write it.

Strings comparing work for every char from the beginning. If first char is equal then check second and so on. In your results "10" < "2", cause unicode of "1"-character is less then "2"-character code.

You need to compare do it like this:

self.sortedTableArray.sort(by: {
   if $0.date != $1.date {
         return $0.date < $1.date
   } else {
         return Int($0.id) ?? 0 < Int($1.id) ?? 0
   }
})

Also your should compare dates as Date not Strings.



回答2:

Just use the string comparator which sorts numeric strings properly

self.sortedTableArray.sort(by: {
    if $0.date != $1.date {
        return $0.date < $1.date
    } else {
        return $0.id.localizedStandardCompare($1.id) == .orderedAscending
    }
})

or the standard compare selector with option .numeric

return $0.id.compare($1.id, options: .numeric) == .orderedAscending


回答3:

You can extend your BalanceUser adding computed properties to return year, month, day and id value. Next just make your struct conform to Comparable protocol:

extension BalanceUser: Comparable {
    var year: Int {
        return Int(date.suffix(4))!
    }
    var month: Int {
        return Int(date.prefix(2))!
    }
    var day: Int {
        return Int(date.prefix(5).suffix(2))!
    }
    var idValue: Int {
        return Int(id)!
    }
    static func ==(lhs: BalanceUser, rhs: BalanceUser) -> Bool {
        return lhs.date == rhs.date && lhs.id == rhs.id
    }
    static func <(lhs: BalanceUser, rhs: BalanceUser) -> Bool {
        return (lhs.year, lhs.month, lhs.day, lhs.idValue) < (rhs.year, rhs.month, rhs.day, rhs.idValue)
    }
}

Now you can simply sort your custom type array:

sortedTableArray.sort()


回答4:

Please check the following code:

let array = ["11-07-2017", "14-07-2017", "10-07-2017","08-07-2017"]
var convertedArray: [Date] = []

var dateFormatter = DateFormatter()
dateFormatter.dateFormat = "dd-MM-yyyy"// yyyy-MM-dd"

for dat in array {
    let date = dateFormatter.date(from: dat)
       if let date = date {
            convertedArray.append(date)
        }
    }

var ready = convertedArray.sorted(by: { $0.compare($1) == .orderedDescending })

print(ready)