How to Sort a string date array - swift

I want to sort an array in ascending order. The dates are in string format
Optional(["2019-07-08", "2019-07-09", "2019-07-10", "2019-07-11", "2019-07-12", "2019-07-02"])
I am trying using below code but it's not working.
aryEventTime = aryEventTime.sorted(by: { ($0 as AnyObject).date.compare($1.date) == ComparisonResult.orderedAscending })
aryEventTime = aryEventTime.sorted(by: {
($0 as AnyObject).date.compare(($1 as AnyObject).date) == .orderedDescending}) as? NSMutableArray

I would not advise making it a practice doing lexicographic sorting on dates. It's simple enough just to parse these strings to proper Date objects and sort on those:
let dateStrings = Optional(["2019-07-08", "2019-07-09", "2019-07-10", "2019-07-11", "2019-07-12", "2019-07-02"])
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd"
let sortedDates = dateStrings?.compactMap(formatter.date(from:)).sorted()

I got the answer:
let sortedArray = aryEventTime.sorted { $0.localizedCaseInsensitiveCompare($1) == ComparisonResult.orderedAscending }

The array of String that you have is,
let arr = ["2019-07-08", "2019-07-09", "2019-07-10", "2019-07-11", "2019-07-12", "2019-07-02"]
Instead of converting the String to Date, you can simply sort this array using sorted(), i.e.
let sortedArr = arr.sorted()
print(sortedArr)
Output:
["2019-07-02", "2019-07-08", "2019-07-09", "2019-07-10", "2019-07-11", "2019-07-12"]
Note: date strings can only be sorted without conversion in special cases such as this one. If the strings were in the format yyyy-dd-MM, for example, then simple string sorting would not work.

Related

Convert value to compare in NSPredicate

I am trying to use NSPredicate with Swift and RealmSwift. I want to filter a Realm collection, with one predicate being date related. The dates are stored as Strings in the format yyyy-MM-dd — how do I convert this to a Date so I can compare it to today, as part of the predicate?
My [non-working] attempt so far:
let today = Date()
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd"
let datePredicate = NSPredicate(format: "nextReview <= %#", argumentArray: [today, dateFormatter]) {
let nextReviewDate = dateFormatter.date(from: $0.nextReview)
return nextReviewDate ?? today <= today
}
...
var cardsToReview: [RealmCard] = Cards.filter(compoundPredicate).map { $0 }
You are making things more complicated than needed. Since you have the date format "yyyy-MM-dd" in your database you can directly compare the strings since they will always have the same order as when converted to dates.
let today = dateFormatter.string(from: .now)
let datePredicate = NSPredicate(format: "nextReview <= %#", today)

Sorting dictionary with DateComponents keys in swift

Hi Guys I briefly explain my problem.
From my database I get an array of HistoryItem, a custom type that contains a simple Date property inside it:
struct HistoryItem {
let date: Date
let status: Status // Not important for my problem
}
I want to group this data by year and month, I thought the best way was a dictionary with key DateComponents:
// ungroupedHistory is already fetched and is of type [HistoryItem]
var groupedHistory: [DateComponents : [HistoryItem]]
groupedHistory = Dictionary(grouping: ungroupedHistory) { (historyItem) -> DateComponents in
let calendar = Calendar.current
let components = calendar.dateComponents([.year, .month], from: hisoryItem.date)
return components
}
The result is as expected but the problem is that it is unsorted, and it is obvious that this is the case since the dictionary by definition is an unsorted collection.
How can i get a sorted by date copy of this dictionary?
I've tried something like this:
let sortedDict = groupedHistory.sorted {
$0.key.date.compare($1.key.date) == .orderedDescending
}
But I just get an array with keys of type:
[Dictionary<DateComponents, [HistoryItem]>.Element]
Thanks in advance!
You need to do this in 2 steps, first get an array with the dictionary keys sorted
let sortedKeys = groupedHistory.keys.sorted {
$0.year! == $1.year! ? $0.month! < $1.month! : $0.year! < $1.year!
}
and then use that array to access the values in your dictionary in a sorted manner
for key in sortedKeys {
print(groupedHistory[key])
}
A dictionary is unordered by definition.
To get a sorted array you could create a wrapper struct
struct History {
let components : DateComponents
let items : [HistoryItem]
}
then sort the keys and map those to an array of History
let sortedKeys = groupedHistory.keys.sorted{($0.year!, $0.month!) < ($1.year!, $1.month!)}
let history = sortedKeys.map{History(components: $0, items: groupedHistory[$0]!)}

Any way to sort custom object inside array?

I have this array
var bookTimeArray = [BookTime]()
BookTime class contains followings
var time : String = ""
var status : String = ""
var booked_by : String = ""
Now i need to sort the array bookTimeArray by seeing the BookTime.time variable.
Time variable may contain one time from "12AM" to "11PM"
The object need to be sorted in following pattern
["12AM", "1AM", "2AM", "3AM", "4AM", "5AM", "6AM", "7AM", "8AM", "9AM", "10AM", "11AM", "12PM", "1PM", "2PM", "3PM", "4PM", "5PM", "6PM", "7PM", "8PM", "9PM","10PM", "11PM"]
if bookTimeArray has 5 object
bookTimeArray[0].time = "10AM"
bookTimeArray[1].time = "6AM"
bookTimeArray[2].time = "9AM"
bookTimeArray[3].time = "6PM"
bookTimeArray[4].time = "9PM"
Expected output
bookTimeArray[0].time = "6AM"
bookTimeArray[1].time = "9AM"
bookTimeArray[2].time = "10AM"
bookTimeArray[3].time = "6PM"
bookTimeArray[4].time = "9PM"
I cant figure out how to achieve this. Help me out :(
You can do it this way:
// First create a DateFormatter object to convert your time strings to Date objects so you can compare between them.
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "ha"
dateFormatter.locale = Locale(identifier: "en_US_POSIX")
// Then you only need to sort the bookTimeArray by *time* property after converting *time* string to a Date object using the dateFormatter that we've created above.
let sortedBooks = bookTimeArray.sorted { dateFormatter.date(from: $0.time)! < dateFormatter.date(from: $1.time)! }
You can use dateFormatter for creating Date objects from your String property and then you can sort your array by these Dates
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "ha" // format 1AM, 2AM, 12PM, ...
dateFormatter.locale = Locale(identifier: "en_US_POSIX")
Sorting:
bookTimeArray.sort { bookTime1, bookTime2 in
guard let date1 = dateFormatter.date(from: bookTime1.time), let date2 = dateFormatter.date(from: bookTime2.time) else { return false }
return date1 < date2
}
Hope this will help:
// Custom models array
let dataArray = [Class(fileID:1),Class(fileID:2),Class(fileID:3)]
// here is sorting code
dataArray.sorted({ $0.fileID > $1.fileID })

How to handle two possible date formats?

My app calls a web api that sometimes returns json dates in this format:
"2017-01-18T10:49:00Z"
and sometimes in this format:
"2017-02-14T19:53:38.1173228Z"
I can use the following dateformat to convert the 2nd one to a Date object:
formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"
But of course it doesn't work for the 1st one.
I've tried utilities like https://github.com/melvitax/DateHelper to see if it will work, but I haven't found a way to convert a json date (in any format) into a Date object.
Any recommendations?
Try both formats:
let parser1 = DateFormatter()
parser1.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZ"
let parser2 = DateFormatter()
parser2.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZ"
func parse(_ dateString: String) -> Date? {
let parsers = [parser1, parser2]
for parser in parsers {
if let result = parser.date(from: dateString) {
return result
}
}
return nil
}
print(parse("2017-01-18T10:49:00Z"))
print(parse("2017-02-14T19:53:38.1173228Z"))
Also note that the Z in the format is not a literal value.

How to get the first word before a dash in Swift?

I would like to get the first year of this string. The one before the dash -: "2007-2016"
How can I achieve this within Swift 3?
I did some research and there is an function called substring or index but doesn't know which one I should use.
What I want to achieve is a sort function that will sort on year. So I think the best way to do this is using the first year (from year). There are also objects that only contains one year...
Use index(of:) and substring(to:).
Following your comment, I've also added an example to get the second year.
let str = "2007-2016"
if let idx = str.characters.index(of: "-") {
let year1 = str.substring(to: idx)
print(year1)
let year2 = str.substring(from: str.index(after: idx))
print(year2)
}
Two (other) solutions:
let string = "2007-2016"
let year1 = string.components(separatedBy: "-").first!
let year2 : String
if let range = string.range(of: "-") {
year2 = string.substring(to: range.lowerBound)
} else {
year2 = string
}
If there is no dash in the string the result is the whole string.