1

I have an array with a property of string that resembles a date (yyyy-MM-dd) and another property of string that resembles a time (HH:mm).

I am trying to sort the array by date and then time in 1 sweep.

Example:

Array[0].date = 2019-11-18
Array[0].time = 19:00
Array[1].date = 2019-11-18
Array[1].time = 22:00
Array[2].date = 2019-10-14
Array[2].time = 16:00
Array[3].date = 2019-11-16
Array[3].time = 13:00
Array[4].date = 2019-11-16
Array[4].time = 14:00

and i want to achieve

Array[0].date = 2019-11-18
Array[0].time = 22:00
Array[1].date = 2019-11-18
Array[1].time = 19:00
Array[2].date = 2019-10-16
Array[2].time = 14:00
Array[3].date = 2019-10-16
Array[3].time = 13:00
Array[4].date = 2019-11-14
Array[4].time = 16:00.

How can i achieve this using Swift?

Thank you so much for your time!

Drey
  • 33
  • 1
  • 6

3 Answers3

1

This answer picks up on the refinement to the question in the below comment from the OP in response to the answer from @vadian. The actual requirement is to sort football goal times provided by the API. The solution below creates a struct for this data with a calculated variable for actual goal time and then sorts by that.

struct Goal{
   let matchDate: String
   let matchTime: String
   let goalTime: String

   var timeOfGoal: Date {
      let goalComponents = goalTime.components(separatedBy: "+").map{$0.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines.union(CharacterSet.decimalDigits.inverted))}
      let goalSeconds = TimeInterval(60 * goalComponents.compactMap({Int($0)}).reduce(0, +))
      let dateFormatter = DateFormatter()
      dateFormatter.dateFormat = "yyyy-MM-dd HH:mm"
      let startTime = dateFormatter.date(from: matchDate + " " + matchTime)!
      return startTime.addingTimeInterval(goalSeconds)
   }
}

I tested this as below

let goals = [
   Goal(matchDate: "2019-11-18", matchTime: "22:00", goalTime: "90 +7"),
   Goal(matchDate: "2019-11-18", matchTime: "19:00", goalTime: "22"),
   Goal(matchDate: "2019-11-18", matchTime: "22:00", goalTime: "99"),
   Goal(matchDate: "2019-11-18", matchTime: "19:00", goalTime: "45 + 3"),
   Goal(matchDate: "2019-11-18", matchTime: "19:00", goalTime: "45+6"),
   Goal(matchDate: "2019-11-18", matchTime: "22:00", goalTime: "90+6"),
   Goal(matchDate: "2019-11-18", matchTime: "22:00", goalTime: "35"),
   Goal(matchDate: "2019-11-18", matchTime: "22:00", goalTime: "85"),
   Goal(matchDate: "2019-11-18", matchTime: "22:00", goalTime: "90"),
   Goal(matchDate: "2019-11-18", matchTime: "22:00", goalTime: "90+ 8"),
   Goal(matchDate: "2019-11-18", matchTime: "19:00", goalTime: "44")]

let ordered = goals.sorted{$0.timeOfGoal > $1.timeOfGoal}

ordered.forEach{print("\($0.matchDate) - \($0.matchTime) - \($0.goalTime) ")}

and it correctly produced:

2019-11-18 - 22:00 - 99 
2019-11-18 - 22:00 - 90+ 8 
2019-11-18 - 22:00 - 90 +7 
2019-11-18 - 22:00 - 90+6 
2019-11-18 - 22:00 - 90 
2019-11-18 - 22:00 - 85 
2019-11-18 - 22:00 - 35 
2019-11-18 - 19:00 - 45+6 
2019-11-18 - 19:00 - 45 + 3 
2019-11-18 - 19:00 - 44 
2019-11-18 - 19:00 - 22 

There is room for improvement by not force unwrapping the Date?, although the string cleaning makes this reasonably safe, and by using a class-level static DateFormatter. But I'll leave that refinement for the implementation :-)

flanker
  • 3,840
  • 1
  • 12
  • 20
0

You could append the date and time strings, because the largest time interval (year) is on the left, and smallest (minute) is on the right. Sorting by standard lexicographical means will put the “largest” date / time combination first.

let sortedArray = myArray.sorted(by: { ($0.date + $0.time) > ($1.date + $1.time) })
Chris
  • 4,009
  • 3
  • 21
  • 52
0

First of all please name variables with starting lowercase letter (array)

You can simply sort the array by concatenating the strings because the format yyyy-MM-dd HH:mm is sortable.

array.sort{"\($0.date) \($0.time)" > "\($1.date) \($1.time)"}
vadian
  • 274,689
  • 30
  • 353
  • 361
  • Yes, sorry, i am naming my variables starting lowecase in my code. This was just a fictive example. Ok, i got it, it makes sense what you’re saying, you a genious!! But i have another problem. Let me be more concrete, i am building a football scores app and i need to show the goals live when they happen and i use an api that gives me 3 variables, match_date, match_time and score_time. How do i sort so that i get the latest goal scored at the top of the list? – Drey Nov 19 '19 at 15:50
  • That's another question and the information might be *more concrete* but not concrete enough. What does `score_time` contain? Another string or an `Int` representing the minute when the goal was scored relative to the match time – vadian Nov 19 '19 at 15:56
  • It is a string that contains the minute when the goal was scored like: “87” or “23” or “90+2” – Drey Nov 19 '19 at 15:57
  • This isn't great. String sorts for date/time are flaky, because changing the storage format would quietly break the sorting (and only for some data). Also, it's slower than just using time addition operations, which could hurt depending on the number of element. – Alexander Nov 19 '19 at 16:05
  • @Alexander Actually *changing the storage format would break* any code. – vadian Nov 19 '19 at 16:08
  • @vadian Ah yes, I misspoke a bit. The issue here is the coupling between *storage* format and *presentation* format. With something like `NSDate`, the storage is presentation-agnostic, so no changes to front-end presentation/formatting of dates would cause your `NSDate`-based calculations to fail. Not so here, where the two are coupled. – Alexander Nov 19 '19 at 17:36