I am building a bus arrival time application. It needs a function to which two arguments are passed: a current stop which the user is at, and a destination stop which the user wants to go to. The arrival times are hard-coded, and there are no "live arrival" times of any sort.
The problem I am having is trying to compare the times and work out when the next bus is arriving. The times are stored in an array and cannot be changed.
For example, if the array is as follows: ["08:00", "23:00", "01:00", "04:00"] and also, let us say the current time is "16:00", the time returned by the function is "23:00". Simple, right? I have coded this bit already with an extension class which can be found in my Pastebin.
However, the problem arises when the time passes into "the next day", so if the time is "00:00", I don't know how to return "01:00", since my function will only return the first time in the array ("08:00") since "00:00" is lower than "08:00" .
import UIKit
// Replace the variable currentTime with a value of "00:00" and see how my function returns "08:17" which is wrong. I want the function to return "00:03" since it is the next time in the array. Or if the current time is "01:00" it should return "01:03". BUT, if the current time is "01:04" or greater, it should return the first time in the array "08:17"
// Hard coded bus arrival times
let array: [String] = ["08:17", "08:37", "08:57", "09:21", "09:51", "10:21", "10:51", "11:21", "11:51", "12:21", "12:51", "13:21", "13:51", "14:21", "14:51", "15:21", "15:51", "16:21", "16:51", "17:21", "17:51", "18:21", "18:51", "19:21", "19:51", "21:03", "22:03", "23:03", "00:03", "01:03"]
// Date object stuff
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
dateFormatter.timeZone = TimeZone(secondsFromGMT: 0)
let date = Date()
let calender = Calendar.current
let components = calender.dateComponents([.year, .month, .day, .hour, .minute, .second], from: date)
let year = components.year
let month = components.month
let day = components.day
let hour = components.hour
let minute = components.minute
let second = components.second
// Returns current time in a "HH:mm:ss" format
func getTimeString() -> String {
var minuteAsString = String(minute!)
if minute! < 10 {
minuteAsString = "0\(String(minute!))"
}
let timeString = String(hour!) + ":" + minuteAsString
return timeString
}
func getNextBus(_ currentStop: String,_ destinationStop: String) -> String {
var listToUse: [String] = []
let currentTime = getTimeString()
print(currentTime)
switch (currentStop, destinationStop) {
case ("stop1", "stop2"):
listToUse = array
default: ()
}
print(listToUse)
for busTime in listToUse {
if currentTime < busTime {
return busTime
}
}
return "Error! No time found."
}
print(getNextBus("stop1", "stop2"))
// Time class which allows times to be compared and equated
class Time: Comparable, Equatable {
init(_ date: Date) {
//get the current calender
let calendar = Calendar.current
//get just the minute and the hour of the day passed to it
let dateComponents = calendar.dateComponents([.hour, .minute], from: date)
//calculate the seconds since the beggining of the day for comparisions
let dateSeconds = dateComponents.hour! * 3600 + dateComponents.minute! * 60
//set the varibles
secondsSinceBeginningOfDay = dateSeconds
hour = dateComponents.hour!
minute = dateComponents.minute!
}
init(_ hour: Int, _ minute: Int) {
//calculate the seconds since the beggining of the day for comparisions
let dateSeconds = (hour * 3600 + minute * 60)
//set the variables
secondsSinceBeginningOfDay = dateSeconds
self.hour = hour
self.minute = minute
}
var hour : Int
var minute: Int
var date: Date {
//get the current calender
let calendar = Calendar.current
//create a new date components.
var dateComponents = DateComponents()
dateComponents.hour = hour
dateComponents.minute = minute
return calendar.date(byAdding: dateComponents, to: Date())!
}
/// the number or seconds since the beginning of the day, this is used for comparisions
public let secondsSinceBeginningOfDay: Int
static func < (lhs: Time, rhs: Time) -> Bool {
return lhs.secondsSinceBeginningOfDay < rhs.secondsSinceBeginningOfDay
}
}```
Suppose we have this array:
let array = ["08:00", "23:00", "01:00", "04:00"]
A more convenient way of dealing with "bus times" would be to define a struct like so:
struct BusTime: Comparable, CustomStringConvertible {
let hour : Int
let minute : Int
static func < (lhs: BusTime, rhs: BusTime) -> Bool {
return (lhs.hour, lhs.minute) < (rhs.hour, rhs.minute)
}
var description: String {
get {
let formatter = NumberFormatter()
formatter.minimumIntegerDigits = 2
return formatter.string(for: hour)! + ":" + formatter.string(for: minute)!
}
}
}
NB: In the rest of the answer I'll be force-unwrapping for brevity)
Let's create a sorted array of BusTime
s:
let busTimes: [BusTime] = array.map { str in
return BusTime(hour: Int(str.prefix(2))!, minute: Int(str.suffix(2))!)
}
var sortedBusTimes = busTimes.sorted()
Let's also define a variable nextBus
which represents the next bus time:
var nextBus: BusTime = sortedBusTimes[0]
Now, let's create a time that corresponds to say the current date:
let nowComps = Calendar.current.dateComponents([.hour, .minute], from: Date())
let now = BusTime(hour: nowComps.hour!, minute: nowComps.minute!)
With binary search, we'll be able to find the next bus time in O(log(n)):
var low = sortedBusTimes.startIndex
var high = sortedBusTimes.endIndex
while low < high {
let middle = low + (high - low)/2
let middleTime = sortedBusTimes[middle]
if middleTime == now {
low = middle
break
} else if middleTime < now {
low = middle + 1
} else if now < middleTime {
high = middle
}
}
if low != sortedBusTimes.endIndex, high != 0 {
nextBus = sortedBusTimes[low]
}
The definition middle
could be simpler this way:
let middle = low + (high - low)/2
But take this article into consideration.
Finally, let's check:
print(nextBus)
At the time of writing this answer, it is 17:52. So the result printed in the console is:
23:00
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.