Reputation: 13
I have an array of objects in Javascript, which contain an event start and end moment as milliseconds. Our codebase currently uses a naive search algorithm that iterates through the array until we find an event that contains a moment in time:
// time contains the moment of time we're looking to find an event for
profile.tempbasaltreatments.forEach( function eachTreatment (t) {
if (time <= t.endmills && time >= t.mills) {
return t;
}
});
which doesn't cut it with the performance with larger datasets. What'd be a good algorithm / data model to efficiently go through the object array to find an event that encapsulates a moment in time? You can assume that in case the events overlap, first match is always sufficient.
Upvotes: 1
Views: 897
Reputation: 350770
I would suggest these pre-processing steps (before searching):
Then the search would be binary, as follows:
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());
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.
Upvotes: 2
Reputation: 18803
If the events are sorted by start time, "mid time" or end time, you could use a binary search to find one close by, then do a local linear search to find one that includes the time stamp (the local search direction depends on the sort order: If the events are ordered by start time, you need to look in the direction of decreasing start time, starting at the closest start time).
The main problem with this approach is that it's slow when there is no maximum event duration, as this means that there is no stop criterion for the local search other than reaching the end of the list.
A better approach might be to store the events in a data structure suitable for efficient access, e.g. an interval tree.
Upvotes: 0