简体   繁体   中英

How can I compare times in an array?

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM