First, here's a solution that works if all the lists contain exactly one item.
def simple_chain(graph, key):
chain = [key]
while True:
lst = graph.get(key)
if lst is None:
# There's nothing left to add to the chain
break
key = lst[0]
if key in chain:
# We've found a loop
break
chain.append(key)
return chain
customer_provider = {
'2': ['5'], '1': ['2'], '3': ['4'], '4': ['2'],
}
data = customer_provider
for k in data:
print(simple_chain(data, k))
output
['2', '5']
['1', '2', '5']
['3', '4', '2', '5']
['4', '2', '5']
The general solution is a little harder. We can create all the chains using a recursive generator. The code below works, but it's not very efficient, since it makes the same sub-chains multiple times.
The basic strategy is similar to the previous version, but we need to loop over all the keys in each list, and make a new chain for each one.
To fully understand how this code works you need to be familiar with recursion and with Python generators. You may also find this page helpful: Understanding Generators in Python; there are also various Python generators tutorials available online.
def make_chains(graph, key, chain):
''' Generate all chains in graph that start at key.
Stop a chain when graph[key] doesn't exist, or if
a loop is encountered.
'''
lst = graph.get(key)
if lst is None:
yield chain
return
for k in lst:
if k in chain:
# End this chain here because there's a loop
yield chain
else:
# Add k to the end of this chain and
# recurse to continue the chain
yield from make_chains(graph, k, chain + [k])
customer_provider = {
'2': ['5'], '1': ['2'], '3': ['4'], '4': ['2'],
}
pm_data = {
'2': ['5'], '1': ['2'], '3': ['4', '6'],
'4': ['2'], '6': ['1', '5'],
}
#data = customer_provider
data = pm_data
for k in data:
for chain in make_chains(data, k, [k]):
print(chain)
If we run that code with data = customer_provider
it produces the same output as the previous version. Here's the output when run with data = pm_data
.
['2', '5']
['1', '2', '5']
['3', '4', '2', '5']
['3', '6', '1', '2', '5']
['3', '6', '5']
['4', '2', '5']
['6', '1', '2', '5']
['6', '5']
The yield from
syntax is a Python 3 feature. To run this code on Python 2, change
yield from make_chains(graph, k, chain + [k])
to
for ch in make_chains(graph, k, chain + [k]):
yield ch
Prior to Python 3.6 dict
does not retain the insertion order of keys, so for k in data
can loop over the keys of data
in any order. The output lists will still be correct, though. You may wish to replace that loop with
for k in sorted(data):
to get the chains in order.