0

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)
 }
asked Jan 11, 2018 at 4:31
14
  • 4
    It is sorting properly. Your ids are strings so they are sorted alphabetically, not numerically. Commented Jan 11, 2018 at 4:32
  • Oh, I did not realize that. So if I store the id as an integer and sort it that way, it will work correctly, right? Commented Jan 11, 2018 at 4:33
  • 4
    BTW - your dates won't sort property as strings in that format. They need to be formatted as yyyy-MM-dd to sort properly as strings. Commented Jan 11, 2018 at 4:34
  • If you want your ids treated as numbers then don't use a string. Use a numeric data type. Or use code that sorts the strings as numbers. Commented Jan 11, 2018 at 4:35
  • 1
    No, your dates are not sorting correctly. Add a date of 02-15-2018 and see what happens. Or 12-25-2016. Commented Jan 11, 2018 at 4:38

4 Answers 4

3

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.

answered Jan 11, 2018 at 6:57
1

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
answered Jan 11, 2018 at 8:25
1

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()
answered Jan 11, 2018 at 5:57
8
  • Not really any reason to separately compare the year, month, and day in the == operator. Just compare date and id as-is. Commented Jan 11, 2018 at 5:59
  • @rmaddy thats true Commented Jan 11, 2018 at 6:00
  • 1
    Tuples can be used to sort by multiple criteria, see stackoverflow.com/a/37612765/1187415. – In this case: return (lhs.year, lhs.month, lhs.day) < (rhs.year, rhs.month, rhs.day) Commented Jan 11, 2018 at 6:20
  • 2
    Or (lhs.year, lhs.month, lhs.day, lhs.idValue) < (rhs.year, rhs.month, rhs.day, rhs.idValue) Commented Jan 11, 2018 at 6:27
  • 1
    GitHub seems to be offline. And yes, it is implemented for tuples up to arity 6. Commented Jan 11, 2018 at 15:21
0

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)
shim
10.3k13 gold badges78 silver badges121 bronze badges
answered Jan 11, 2018 at 4:42
1
  • This answer completely misses the issue of sorting the id part which is what is actually causing the issue. The date part is sorting correctly. Commented Jan 11, 2018 at 9:53

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.