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]]
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:
The events are sorted by start time.
An event can only overlap at most one prior event.
If either of these assumptions is violated, the functions will return wrong answers.