Reputation: 9522
I have a bunch of time slots with a start and an end of type datetime. Given a particular datetime, I need to get those that are before, and after:
def get_before(timeslots, moment):
return [t for t in timeslots if t.end <= moment]
def get_after(timeslots, moment):
return [t for t in timeslots if t.start >= moment]
But there are two optional arguments, minimum and maximum, indicating that the time slots must be within the maximum amount of minutes and outside the minimum.
We have the following slots:
2018-06-27 09:00:00 - 2018-06-27 10:00:00
2018-06-27 10:00:00 - 2018-06-27 11:00:00
2018-06-27 11:00:00 - 2018-06-27 12:00:00
2018-06-27 12:00:00 - 2018-06-27 13:00:00
2018-06-27 13:00:00 - 2018-06-27 14:00:00
2018-06-27 14:00:00 - 2018-06-27 15:00:00
2018-06-27 15:00:00 - 2018-06-27 16:00:00
2018-06-27 16:00:00 - 2018-06-27 17:00:00
2018-06-27 17:00:00 - 2018-06-27 18:00:00
2018-06-27 18:00:00 - 2018-06-27 19:00:00
2018-06-27 19:00:00 - 2018-06-27 20:00:00
2018-06-27 20:00:00 - 2018-06-27 21:00:00
2018-06-27 21:00:00 - 2018-06-27 22:00:00
If we want the time slots after 2018-06-27 15:00:00
, with a minimum of 1 hour and a maximum of 4 hours, we get:
2018-06-27 16:00:00 - 2018-06-27 17:00:00
2018-06-27 17:00:00 - 2018-06-27 18:00:00
2018-06-27 18:00:00 - 2018-06-27 19:00:00
2018-06-27 19:00:00 - 2018-06-27 20:00:00
This is my implementation:
def get_before(timeslots, moment, minimum=None, maximum=None):
tslots = [t for t in timeslots if t.end <= moment]
if maximum is not None:
maxdelta = datetime.timedelta(minutes=maximum)
tslots = [t for t in tslots if t.end + maxdelta >= moment]
if minimum is not None:
mindelta = datetime.timedelta(minutes=minimum)
tslots = [t for t in tslots if t.end <= moment - mindelta]
return tslots
def get_after(timeslots, moment, minimum=None, maximum=None):
tslots = [t for t in timeslots if t.start >= moment]
if maximum is not None:
maxdelta = datetime.timedelta(minutes=maximum)
tslots = [t for t in tslots if t.start - maxdelta <= moment]
if minimum is not None:
mindelta = datetime.timedelta(minutes=minimum)
tslots = [t for t in tslots if t.start >= moment + mindelta]
return tslots
The problem is that for each filtering function, I am iterating the timeslots
list three times: one to get those before or after the moment, secondly to get those within the maximum time range, and thirdly to filter those outside the minimum time range.
These function are going to be called very frequently, so I wonder if there is a way I could merge the filtering so that the list is iterated only once.
Upvotes: 0
Views: 332
Reputation: 1281
just compute everything once and set useful default values:
def get_before2(timeslots, moment, minimum=0, maximum=1440):
maxdelta = moment - datetime.timedelta(minutes=maximum)
mindelta = moment - datetime.timedelta(minutes=minimum)
tslots = [t for t in timeslots if t.end >= maxdelta and t.end <= mindelta]
Upvotes: 0
Reputation: 6030
You can create a lambda function based on the presences of maximum and minimum. Then apply that function in a single list comprehension.
maximum = 10
minimum = 5
if maximum:
f = lambda x : x <= maximum
if minimum:
f = lambda x : x >= minimum
if maximum and minimum:
f = lambda x : x <= maximum and x >= minimum
print [ x for x in range(50) if f(x) ]
output:
[sri@localhost ~]$ python test.py
[5, 6, 7, 8, 9, 10]
Also, if your times are sorted you can stop searching once you find the maximum.
Upvotes: 0
Reputation: 108
This can be solved effeciently with an interval tree. See https://en.wikipedia.org/wiki/Interval_tree
There seem to be many python implementations according to a quick google search.
Upvotes: 1