I am trying to obtain all possible ways to go from a city A to a city B on a given day by plane, with a maximum of 2 stops.
I have as input a list of dictionaries:
flights = [
...
{'dep': 'FRA', 'arr': 'AMS', 'dep_dt': '2017-05-01 12:00:00', 'arr_dt': '2017-05-01 13:15:00', 'price': 100},
{'dep': 'FRA', 'arr': 'CPH', 'dep_dt': '2017-05-01 10:00:00', 'arr_dt': '2017-05-01 12:00:00', 'price': 80},
{'dep': 'FRA', 'arr': 'MAD', 'dep_dt': '2017-05-01 09:00:00', 'arr_dt': '2017-05-01 10:50:00', 'price': 30},
{'dep': 'CPH', 'arr': 'AMS', 'dep_dt': '2017-05-01 15:00:00', 'arr_dt': '2017-05-01 16:30:00', 'price': 60},
{'dep': 'CPH', 'arr': 'MAD', 'dep_dt': '2017-05-01 14:15:00', 'arr_dt': '2017-05-01 17:10:00', 'price': 70},
{'dep': 'MAD', 'arr': 'AMS', 'dep_dt': '2017-05-01 19:00:00', 'arr_dt': '2017-05-01 21:40:00', 'price': 20},
...
]
With thousands of records between many cities on many dates.
Example
Let's say I want all possibilities to go from FRA to AMS. To find all possible trips, I've split the problem in 3:
- Direct flights
- Flights with 1 stopover
- Flights with 2 stopovers
(I omit the date constraints below for simplicity)
In order to get the direct flights, I can do:
direct_flights = [f for f in flights if f['dep']=='FRA' and f['arr']=='AMS']
To get trips with one stopover, I've gone for a nested loop. Not optimal, but it works:
first_segment = [f for f in flights if f['dep']=='FRA']
second_segment = [f for f in flights if f['arr']=='AMS']
flights_w_1stop = []
for s1 in first_segment:
for s2 in second_segment:
if (s1['arr'] == s2['dep'] and
s1['arr_dt'] < s2['dep_dt']):
flights_w_1stop.append((s1, s2))
Now, to get all trips with two stopovers, I only could come up with a similar approach than above, but this makes two nested loops, which looks horrible and will be very slow when I have many flights.
first_segment = [f for f in flights if f['dep']=='FRA']
second_segment = [f for f in flights]
third_segment = [f for f in flights if f['arr']=='AMS']
flights_w_2stops = []
for s1 in first_segment:
for s2 in second_segment:
for s3 in third_segment:
if (s1['arr'] == s2['dep'] and
s2['arr'] == s3['dep'] and
s1['arr_dt'] < s2['dep_dt'] and
s2['arr_dt'] < s3['dep_dt']):
flights_w_2stops.append((s1, s2, s3))
Finally, I would merge all 3 different lists with the same data structure and sort them either by price or by total duration.
Question
How could this be done more efficiently?
Note: I have read about graphs, but I don't think I will ever want to consider more than two stopovers. In addition, it would mean learning a new methodology. It is always good to learn, so if it is the best option I would invest the time. But if a similar thing can be done in another way (rearranging the nested loops somehow?), I would prefer so.
I found a similar question in SO and CodeReview, but I do not see how to apply that to my case.