Dependencies are expressed as a dictionary whose keys are items and whose values are a set of dependent items. Output is a list of sets in topological order. The first set consists of items with no dependences, each subsequent set consists of items that depend upon items in the preceeding sets. >>>
(data)
| 1 | def toposort2(data): |
| 2 | """Dependencies are expressed as a dictionary whose keys are items |
| 3 | and whose values are a set of dependent items. Output is a list of |
| 4 | sets in topological order. The first set consists of items with no |
| 5 | dependences, each subsequent set consists of items that depend upon |
| 6 | items in the preceeding sets. |
| 7 | |
| 8 | >>> print '\\n'.join(repr(sorted(x)) for x in toposort2({ |
| 9 | ... 2: set([11]), |
| 10 | ... 9: set([11,8]), |
| 11 | ... 10: set([11,3]), |
| 12 | ... 11: set([7,5]), |
| 13 | ... 8: set([7,3]), |
| 14 | ... }) ) |
| 15 | [3, 5, 7] |
| 16 | [8, 11] |
| 17 | [2, 9, 10] |
| 18 | |
| 19 | """ |
| 20 | |
| 21 | from functools import reduce |
| 22 | |
| 23 | # Ignore self dependencies. |
| 24 | for k, v in data.items(): |
| 25 | v.discard(k) |
| 26 | # Find all items that don't depend on anything. |
| 27 | extra_items_in_deps = reduce(set.union, data.itervalues()) - set(data.iterkeys()) |
| 28 | # Add empty dependences where needed |
| 29 | data.update({item:set() for item in extra_items_in_deps}) |
| 30 | while True: |
| 31 | ordered = set(item for item, dep in data.iteritems() if not dep) |
| 32 | if not ordered: |
| 33 | break |
| 34 | yield ordered |
| 35 | data = {item: (dep - ordered) |
| 36 | for item, dep in data.iteritems() |
| 37 | if item not in ordered} |
| 38 | assert not data, "Cyclic dependencies exist among these items:\n%s" % '\n'.join(repr(x) for x in data.iteritems()) |