I would suggest these pre-processing steps (before searching):
- Optionally take a copy of the original array, if needed for other purposes;
- Sort the array by event start times. If possible, this should really be done by the database, which can maintain an index for that.
- Remove events from the array that have an end time that comes before the previous event's end time. When a time would match with this removed event, it can also match with the previous event. Since matching any event is good enough, we can remove this event.
Then the search would be binary, as follows:
- Set range of search to the whole array. A range is expressed as a start and end index in the array (not as two times)
- Take the middle element of that range
- If this event matches the given time, exit with success
- If this event has a start time greater than the given time, repeat from step 2 with the half of the range that comes after the selected element
- Otherwise take the other half of the range (before the selected element) and repeat from 2
- Stop repeating if the range has no more events and exit with failure.
The pre-processing should be done only once, which has time complexity O(n log n) if you still need to sort, otherwise it is O(n). Once that is done you can repeatedly find events in O(log n) time.
Here is some JavaScript code for the above:
// Create a copy of the original array and sort it by event start date
var events = profile.tempbasaltreatments.slice(0).sort(function (a, b) {
return a.mills - b.mills;
});
// Remove events that fall completely within the limits of another event.
// They are not needed to find an event for a given time.
for (var i = 0; i < events.length-1;) {
if (i && events[i].endmills < events[i-1].endmills) {
events.splice(i, 1);
} else {
i++;
};
}
// Now also the remaining events' end dates are sorted
// function for fast search in events:
function findEvent(events, time) {
// Binary search for event
var first = 0, last = events.length - 1;
while (first <= last) {
var i = first + Math.floor((last - first) / 2);
var t = events[i];
if (time >= t.mills && time <= t.endmills) return t;
if (time < t.mills) {
last = i - 1;
} else { // time > t.endmills
first = i + 1;
}
}
// returns undefined
}
// Example call: find a currently running event:
var foundEvent = findEvent(events, new Date().getTime());
Addendum
Here is how the filtering happens in the last pre-processing step. First a timeline of how events are ordered after sorting on start time:
a: ---------------
b: -------
c: ------------------
d: --
e: --
f: -----
The events that can be eliminated are b:
a: ---------------
c: ------------------
d: --
e: --
f: -----
....then d:
a: ---------------
c: ------------------
e: --
f: -----
...then e:
a: ---------------
c: ------------------
f: -----
... and f:
a: ---------------
c: ------------------
It is clear that the total covered period is the same as in the original, before the filtering.