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?
>Solution :
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
note on datetime
Using datetime without date will default to 1970-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(1970,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()