I have an array containing an object called HistoryObject
and it has properties such as "date", "name", etc.
I am sorting the array like so:
let sortedArray = HistoryArray.sort({ $0.date.compare($1.date) == NSComparisonResult.OrderedDescending})
which is supposed to sort the date from newer to oldest. For example:
- Jun 30, 2016
- Jun 29, 2016
etc..
But when my array contains "Jul 2, 2016" the sorted array becomes:
- Jun 30, 2016
- Jun 29, 2016
- Jul 2, 2016
Where "Jul 2, 2016" should be on top after sorting, now it's on the bottom? How can I fix this problem?
Using Swift 4 & Swift 3
let testArray = ["25 Jun, 2016", "30 Jun, 2016", "28 Jun, 2016", "2 Jul, 2016"]
var convertedArray: [Date] = []
var dateFormatter = DateFormatter()
dateFormatter.dateFormat = "dd MM, yyyy"// yyyy-MM-dd"
for dat in testArray {
let date = dateFormatter.date(from: dat)
if let date = date {
convertedArray.append(date)
}
}
var ready = convertedArray.sorted(by: { $0.compare($1) == .orderedDescending })
print(ready)
Using Swift 2
For example you have the array with dates and another 1 array, where you will save the converted dates:
var testArray = ["25 Jun, 2016", "30 Jun, 2016", "28 Jun, 2016", "2 Jul, 2016"]
var convertedArray: [NSDate] = []
After that we convert the dates:
var dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "dd MM, yyyy"// yyyy-MM-dd"
for dat in testArray {
var date = dateFormatter.dateFromString(dat)
convertedArray.append(date!)
}
And the result:
var ready = convertedArray.sort({ $0.compare($1) == .OrderedDescending })
print(ready)
For Swift 3
var testArray = ["25 Jun, 2016", "30 Jun, 2016", "28 Jun, 2016", "2 Jul, 2016"]
var convertedArray: [Date] = []
var dateFormatter = DateFormatter()
dateFormatter.dateFormat = "dd/MM/yyyy"
for dat in testArray {
var date = dateFormatter.date(from: dat)
convertedArray.append(date!)
}
//Approach : 1
convertedArray.sort(){$0 < $1}
//Approach : 2
convertedArray.sorted(by: {$0.timeIntervalSince1970 < $1.timeIntervalSince1970})
print(convertedArray)
Avoiding extra variable of convertedArray
Using Swift 4 & Swift 3
let testArray = ["25 Jun, 2016", "30 Jun, 2016", "28 Jun, 2016", "2 Jul, 2016"]
var dateFormatter = DateFormatter()
dateFormatter.dateFormat = "dd MM, yyyy"// yyyy-MM-dd"
var ready = convertedArray.sorted(by: { dateFormatter.date(from:$0).compare(dateFormatter.date(from:$1)) == .orderedDescending })
print(ready)
You have an array historyArray
that contains an array of HistoryObject
. Each HistoryObject
contains a date string in the form "MM dd, yyyy"
Edit:
(You want to sort your history objects by their date values. It is a bad idea to try to sort objects with date strings by those date strings, since you have to convert the date strings to Cocoa Date
objects for every comparison, so you end up converting the dates to date objects over and over and over. In a benchmark I did, this causes the sort to run 1200X slower than if you batch-convert your date strings to Date
objects before sorting, as outlined below.)
In order to do that efficiently you need to first get Date
values for all of the objects. One way to do that would be to add a lazy Date
var to your HistoryObject
that is calculated from the date string. If you don't want to do that you can:
- Map your array of history objects to an array of Date objects using
a DateFormatter.
- Use the
zip()
function to combine the array of history objects and
the array of date objects into an array of tuples.
- Sort the array of tuples.
- Map the array of tuples back to an array of history objects.
The code to do that might look something like this:
Version 1
var dateFormatter = DateFormatter()
dateFormatter.dateFormat = "MM dd, yyyy"
//I don't know what your HistoryObject looks like, so I'll fake it.
struct HistoryObject: CustomStringConvertible {
let dateString: String
let value: Int
var description: String {
return "date: \(dateString), value: \(value)"
}
}
//Create an array of date strings.
let testArray = ["Jun 25, 2016", "Jun 30, 2016", "Jun 28, 2016", "Jul 2, 2016"]
//Use the array of date strings to create an array of type [HistoryObject]
let historyArray: [HistoryObject] = testArray.map {
let value = Int(arc4random_uniform(1000))
return HistoryObject(dateString: $0, value: value)
}
print("\n-----> Before sorting <-----")
historyArray.forEach { print($0) }
//Create an array of the `Dates` for each HistoryObject
let historyDates: [Date] = historyArray.map { dateFormatter.date(from: $0.dateString)!
}
//Combine the array of `Dates` and the array of `HistoryObjects` into an array of tuples
let historyTuples = zip(historyArray, historyDates)
//Sort the array of tuples and then map back to an array of type [HistoryObject]
let sortedHistoryObjects = historyTuples.sorted { $0.1 > $1.1}
.map {$0.0}
print("\n-----> After sorting <-----")
sortedHistoryObjects.forEach { print($0) }
If you add a lazy var date
to your HistoryObject the sorting code is a LOT simpler:
Version 2:
//I don't know what your HistoryObject looks like, so I'll fake it.
class HistoryObject: CustomStringConvertible {
let dateString: String
lazy var date: Date = { dateFormatter.date(from: self.dateString)! }()
let value: Int
var description: String {
return "date: \(dateString), value: \(value)"
}
init(dateString: String, value: Int) {
self.dateString = dateString
self.value = value
}
}
//Create an array of date strings.
let testArray = ["Jun 25, 2016", "Jun 30, 2016", "Jun 28, 2016", "Jul 2, 2016"]
//Use the array of date strings to create an array of type [HistoryObject]
let historyArray: [HistoryObject] = testArray.map {
let value = Int(arc4random_uniform(1000))
return HistoryObject(dateString: $0, value: value)
}
print("\n-----> Before sorting <-----")
historyArray.forEach { print($0) }
let sortedHistoryArray = historyArray.sorted { $0.date > $1.date }
print("\n-----> After sorting <-----")
sortedHistoryArray.forEach { print($0) }