I have two lists of times (Hour:Min:Sec format) and I've been struggling to compare each entry in list_a
against all of list_b
to identify values that fall within 30 minutes:
list_a = ["10:26:42", "8:55:43", "7:34:11"]
list_b = ["10:49:20", "8:51:10", "10:34:35", "8:39:47", "7:11:49", "7:42:10"]
Expected Output:
10:26:42 is within 30m of 10:49:20, 10:34:35
8:55:43 is within 30m of 8:51:10, 8:39:47
7:34:11 is within 30m of 7:11:49, 7:42:10
So far what I've been doing is:
import datetime
# Convert the Lists to Datetime Format
for data in list_a:
convert = datetime.datetime.strptime(data,"%H:%M:%S")
list_a_times.append(convert)
for data in list_b:
convert = datetime.datetime.strptime(data,"%H:%M:%S")
list_b_times.append(convert)
# Using a Value of List A, Find the Closest Value in List B
for data in list_a_times:
closest_to_data = min(list_b_times, key=lambda d: abs(d - data))
print(data, closest_to_data)
This kind of works, but it only finds one nearest value! How can I manipulate the min() function to keep providing values as long as they're within the desired 30 minutes or less?
IIUC, you want to compare all combinations, so you need to check all.
Please read the end of the answer for a note on datetime
/timedelta
.
Using itertools.product
:
list_a = ['10:26:42', '8:55:43', '7:34:11']
list_b = ['10:49:20', '8:51:10', '10:34:35', '8:39:47', '7:11:49', '7:42:10']
import datetime
from itertools import product
str2time = lambda s: datetime.datetime.strptime(s, "%H:%M:%S")
for a,b in product(map(str2time, list_a), map(str2time, list_b)):
if abs(a-b).total_seconds() <= 1800:
print(f'{a:%H:%M:%S} is within 30m of {b:%H:%M:%S}')
output:
10:26:42 is within 30m of 10:49:20
10:26:42 is within 30m of 10:34:35
08:55:43 is within 30m of 08:51:10
08:55:43 is within 30m of 08:39:47
07:34:11 is within 30m of 07:11:49
07:34:11 is within 30m of 07:42:10
Using nested for loops:
import datetime
str2time = lambda s: datetime.datetime.strptime(s, "%H:%M:%S")
for a in map(str2time, list_a):
start = f'{a:%H:%M:%S} is within 30m of'
for b in map(str2time, list_b):
if abs(a-b).total_seconds() <= 1800:
print(f'{start} {b:%H:%M:%S}', end='')
start = ','
if start == ',':
print()
output:
10:26:42 is within 30m of 10:49:20, 10:34:35
08:55:43 is within 30m of 08:51:10, 08:39:47
07:34:11 is within 30m of 07:11:49, 07:42:10
datetime
Using datetime
without date will default to 1900-01-01, which can have edge effects close to midnight. Instead, you could use timedelta
objects. With my code you need to change the str2time
function to:
def str2time(s):
h,m,s = map(int, s.split(':'))
return datetime.timedelta(hours=h, minutes=m, seconds
And alter a bit the code to be able to convert to string:
z = datetime.datetime(1900,1,1)
for a in map(str2time, list_a):
start = f'{z+a:%H:%M:%S} is within 30m of'
for b in map(str2time, list_b):
if abs(a-b).total_seconds() <= 1800:
print(f'{start} {z+b:%H:%M:%S}', end='')
start = ','
if start == ',':
print()