arraysswiftoutofrangeexception

How do I solve this in Swift? - 600: Fatal error: Index out of range


I am new to Swift and coding in general. I am trying to get a list of busy periods from multiple events that occur in one day but keep getting "index out of range."

What is wrong here? (I am sorry my code is very messy)

`func busyPeriod() -> [[String:Date]]{

    var busylist: [[String: Date]] = []
    var eventlist: [[String: Date]] = 
      [["start": 2023-02-16 09:00:00, "end": 2023-02-16 10:00:00], 
      ["start": 2023-02-16 10:00:00, "end": 2023-02-16 10:15:00]],
      ["start": 2023-02-16 13:00:00, "end": 2023-02-16 14:00:00]]

    if eventlist.count == 1{
       return eventlist
    }

    for i in eventlist.indices{
        if i == 0 {
            busylist += [["start": eventlist[0]["start"]!, "end": eventlist[0]["end"]!]]
    } else {
        //This part comes out as Thread 1: Fatal Error: Index out of range
        if busylist[-1]["start"]! <= eventlist[i]["start"]!, eventlist[i]["start"]! <= busylist[-1]["end"]! { 
            busylist[-1]["start"] = min(busylist[-1]["start"]!, eventlist[i]["start"]!)
            busylist[-1]["end"] = max(busylist[-1]["end"]!, eventlist[i]["end"]!)
    } else {
        busylist += [["start": eventlist[i]["start"]!, "end": eventlist[i]["end"]!]]
    }}
    return busylist
}

What I expect as an outcome:

    busylist = [
        ["start": 2023-02-16 09:00:00, "end": 2023-02-16 10:15:00],
        ["start": 2023-02-16 13:00:00, "end": 2023-02-16 14:00:00]]

Solution

  • Some languages, like Python, allow you to use a negative index to count back from the end of the array. So in Python, busylist[-1] gives you the last element in the array busylist (if busylist is not empty).

    Swift does not support that behavior.

    Also, your code is needlessly complicated because you're using a [String: Date] instead of a domain-specific type to store a time span.

    I'd write your function more like the following, although to avoid the complication of date parsing, I'm just using String to hold a date (thus violating my own advice to use good types 😅):

    import Foundation
    
    struct TimeSpan: CustomStringConvertible {
        var start: String
        var end: String
        
        var description: String {
            "\(start) - \(end)"
        }
    }
    
    func busyPeriods(for events: [TimeSpan]) -> [TimeSpan] {
        guard var last = events.first else {
            return []
        }
        
        var answer: [TimeSpan] = []
        
        for event in events.dropFirst() {
            if event.start <= last.end {
                last.start = min(last.start, event.start)
                last.end = max(last.end, event.end)
            } else {
                answer.append(last)
                last = event
            }
        }
        
        answer.append(last)
        return answer
    }
    
    print(busyPeriods(for: [
        .init(start: "2023-02-16 09:00:00", end: "2023-02-16 10:00:00"), 
        .init(start: "2023-02-16 10:00:00", end: "2023-02-16 10:15:00"),
        .init(start: "2023-02-16 13:00:00", end: "2023-02-16 14:00:00"),
    ]))
    

    Output:

    [2023-02-16 09:00:00 - 2023-02-16 10:15:00, 2023-02-16 13:00:00 - 2023-02-16 14:00:00]
    

    Note that your function (and my rewrite) make some assumptions about the input:

    If either of these assumptions is violated, the functions will return wrong answers.