Last active
April 1, 2021 01:09
-
-
Save syminical/b2c20ea4ee26889bff707d89e4d9bf2e to your computer and use it in GitHub Desktop.
Scheduling Matchup - I paused the video around 9 min and solved the problem. :) https://youtu.be/kbwk1Tw3OhE
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# https://youtu.be/kbwk1Tw3OhE | |
# I paused the video around 9 min and solved the problem. :) | |
# Take a time tuple and return a 2-tuple of ints representing the length of the input meeting. | |
# e.g. ('10:00', '10:30') => (0, 30) | |
def meeting_len(meeting): | |
start = meeting[0].split(':') | |
end = meeting[1].split(':') | |
return (abs(int(end[0])-int(start[0])), abs(int(end[1])-int(start[1]))) | |
# Take a 2-tuple representing a single time string and return a correctly formatted string representation. | |
# This gets around the single digit conversion issue, and it's important to allow comparisons of time strings: | |
# e.g. (9, 30) => '09:30', not '9:30' | |
def time_str(time): | |
_ = [ f'0{i}' if i < 10 else str(i) for i in time ] | |
return ':'.join(_) | |
# Take a list of time tuples, and a tuple containing the start/end time constraints. | |
# Return a list of time tuples representing the free time gaps between the time tuples in the input list. | |
def free_gaps(cal, ends): | |
ret = [] | |
if not cal: | |
return ends | |
if ends[0] < cal[0][0]: | |
ret.append((ends[0], cal[0][0])) | |
for i in range(len(cal)-1): | |
ret.append((cal[i][1], cal[i+1][0])) | |
if cal[-1][1] < ends[1]: | |
ret.append((cal[-1][1], ends[1])) | |
return ret | |
# Curried function designed to be used with the filter() function. | |
# Takes a minimum time length that meetings must be to pass the filter. | |
# Returns a function that accepts a time tuple that gets tested for the target minimum time length. | |
def min_time(target): | |
def f(meeting): | |
h, m = meeting_len(meeting) | |
return int(h) > 0 or int(m) >= target | |
return f | |
# Take two time tuples and check if they have any intersection. (time in common) | |
# Return a time tuple representing the amount of time in common, or None. | |
def gap_intersection(a, b): | |
if (a[0] < b[1] and b[0] < a[1]): | |
return (max(a[0], b[0]), min(a[1], b[1])) | |
return None | |
def main(): | |
target_meeting_len = 30 | |
# person a | |
cal_a = [('9:00', '10:30'), ('12:00','13:00'), ('14:00', '14:20'), ('14:40', '14:45'), ('16:00', '18:00')] | |
ends_a = ('9:00', '20:00') | |
# person b | |
cal_b = [('10:00', '11:30'), ('12:30', '14:30'), ('14:30', '15:00'), ('16:00', '17:00')] | |
ends_b = ('10:00', '18:30') | |
# Find gaps of free time in a schedule, and filter out gaps shorter than the target meeting length. | |
possible_a = filter(min_time(target_meeting_len), free_gaps(cal_a, ends_a)) | |
possible_b = filter(min_time(target_meeting_len), free_gaps(cal_b, ends_b)) | |
# Find shared gaps of time between two lists of free time gaps, and filter out gaps shorter than the target meeting length. | |
# This loop iterates through two lists, so it's really two while loops in one with a composite iterator. | |
potential_ab = [] | |
gap_a, gap_b = next(possible_a, None), next(possible_b, None) | |
while gap_a and gap_b: | |
# Check for intersection between two free time gaps, and collect gap if there is an intersection. | |
if _ := gap_intersection(gap_a, gap_b): | |
potential_ab.append(_) | |
# Advance iterator(s) | |
if (gap_a[0] <= gap_b[0]): | |
gap_a = next(possible_a, None) | |
if (gap_b[0] <= gap_a[0]): | |
gap_b = next(possible_b, None) | |
# Filter the collected common free time gaps and return the list of common free time gaps where a meeting with the length can fit. | |
return list(filter(min_time(target_meeting_len), potential_ab)) | |
if __name__ == "__main__": | |
print(main()) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment