Skip to content

Commit 4b9e466

Browse files
committed
Adding files to repo
1 parent be93dc8 commit 4b9e466

File tree

4 files changed

+1041
-0
lines changed

4 files changed

+1041
-0
lines changed

Dependencies.py

Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
import collections
2+
3+
4+
class Dependencies(object):
5+
"""Orders a dictionary of items to their dependencies such that all
6+
dependencies resolve. Initialize the class with the [items] object:
7+
8+
{
9+
'item1': ['dependency1', 'dependency2', ...],
10+
'item2': ['dependency1', 'dependency2', ...],
11+
...
12+
}
13+
14+
Alternatively, the class can be intiailized with no items, and items can
15+
be added with the add_item method.
16+
17+
The order method returns an OrderedDict of self.items in an order that
18+
resolves all dependencies, and the order_list method does the same but
19+
simply returns a list of item names in order.
20+
21+
This class also ensures that all dependencies exist, and no circular
22+
dependencies exist. These checks happen when calling the order or order_list
23+
methods.
24+
"""
25+
26+
def __init__(self, items = {}):
27+
"""Perform dependency existance and no circular dependencies checks
28+
when initializing a Dependencies object.
29+
"""
30+
assert isinstance(items, dict), '[items] must be a dict'
31+
assert self.dependencies_exist(items), \
32+
'One or more dependencies do not exist in [items]'
33+
assert self.no_circular_dependencies(items), \
34+
'One or more circular dependencies exist in [dependencies]'
35+
self.items = items.copy()
36+
37+
38+
@staticmethod
39+
def dependencies_exist(my_items):
40+
"""Check if the initial dependencies for the items in [my_items] exist
41+
in [my_items].
42+
"""
43+
all_dependencies_exist = True
44+
for item, dependencies in my_items.items():
45+
for dependency in dependencies:
46+
if dependency not in my_items.keys():
47+
print 'Non-existant dependency: ({0}, {1})'.format(
48+
item, dependency)
49+
all_dependencies_exist = False
50+
return all_dependencies_exist
51+
52+
53+
@staticmethod
54+
def list_dependencies(my_items, item):
55+
"""Find all dependencies for [item] within [my_items].
56+
"""
57+
dependencies, new_dependencies_count = list(my_items[item]), -1
58+
while new_dependencies_count != 0:
59+
new_dependencies_count = 0
60+
for item in dependencies:
61+
new_dependencies = [new_item for new_item in my_items[item]
62+
if new_item not in dependencies]
63+
dependencies += new_dependencies
64+
new_dependencies_count += len(new_dependencies)
65+
return dependencies
66+
67+
68+
def no_circular_dependencies(self, my_items):
69+
"""Check for any circular dependencies in [my_items].
70+
"""
71+
not_circular = True
72+
for item in my_items.keys():
73+
dependencies = self.list_dependencies(my_items, item)
74+
if item in dependencies:
75+
not_circular = False
76+
print 'Circular dependency for', item
77+
return not_circular
78+
79+
80+
def add_item(self, item, dependencies = []):
81+
"""Add a new item to self.dependencies, along with optional dependencies
82+
for that item.
83+
"""
84+
if self.items.has_key(item):
85+
raise Exception('{0} already an item!'.format(str(item)))
86+
else:
87+
self.items[item] = dependencies
88+
89+
90+
def remove_item(self, item):
91+
"""Add a new item to self.dependencies, along with optional dependencies
92+
for that item.
93+
"""
94+
if not self.items.has_key(item):
95+
raise Exception('{0} does not exist!'.format(str(item)))
96+
else:
97+
del self.items[item]
98+
99+
100+
def modify_item(self, item, dependencies = []):
101+
"""Modify the dependencies for an item.
102+
"""
103+
if not self.items.has_key(item):
104+
raise Exception('{0} does not exist!'.format(str(item)))
105+
else:
106+
self.items[item] = dependencies
107+
108+
109+
def order_dependencies(self, my_items):
110+
"""Order the dependencies in [my_items], returning a list of keys from
111+
[my_items] in order such that all dependencies resolve.
112+
"""
113+
items, index = my_items.keys(), 0
114+
while (len(items) - 1) != index:
115+
item = items[index]
116+
current_dependencies = self.list_dependencies(my_items, item)
117+
no_order_change = True
118+
for dependency in current_dependencies:
119+
if items.index(dependency) - items.index(item) > 0:
120+
items += [items.pop(index)]
121+
no_order_change = False
122+
break
123+
if no_order_change:
124+
index += 1
125+
return items
126+
127+
128+
def order(self):
129+
"""Return self.items as an OrderedDict in an order that resolves
130+
all dependencies.
131+
"""
132+
self.__init__(self.items)
133+
if self.items:
134+
ordered_items = self.order_dependencies(self.items)
135+
return collections.OrderedDict(
136+
[(item, self.items[item]) for item in ordered_items])
137+
else:
138+
return {}
139+
140+
141+
def order_list(self):
142+
"""Returns the keys of self.items (the item names) in an order
143+
that resolves all dependencies.
144+
"""
145+
self.__init__(self.items)
146+
if self.items:
147+
return self.order_dependencies(self.items)
148+
else:
149+
return []
150+
151+
152+
def __str__(self):
153+
return str(self.items)
154+
155+
156+
__repr__ = __str__

README.md

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
# Dependency Algorithm
2+
3+
Here is my take on an algorithm in Python that resolves dependencies. I walk through the process of creating the algorithm in `dependency_algo.ipynb`, and implement it in a `Dependencies` class in `Dependencies.py`. The `Dependencies` class accepts as an input a dictionary of items as keys and lists of dependencies as values (see below). Alternatively, you may use the `add_item` method to add items to a `Dependencies` object.
4+
5+
I did not look at other dependency algorithms when creating this, but afterwards I found some [great work by Ferry Boender](http://www.electricmonk.nl/log/2008/08/07/dependency-resolving-algorithm/) that used recursion to resolve dependencies. My solutions do not use recursion.
6+
7+
## Example
8+
9+
Dictionary of items we want to resolve dependencies for:
10+
11+
```python
12+
my_items = {
13+
'A': ['B', 'C', 'D'], # -- A is dependent on B, C, D,
14+
'B': [], # -- B is dependent on nothing, etc.
15+
'C': ['D'],
16+
'D': ['B', 'E'],
17+
'E': ['F'],
18+
'F': [],
19+
'Z': ['A', 'B', 'C', 'D']
20+
}
21+
```
22+
23+
We can use the `Dependencies` class to take these items and order them.
24+
25+
```python
26+
import collections
27+
from Dependencies import Dependencies
28+
29+
# Creating a Dependencies object
30+
dependencies = Dependencies(my_items)
31+
32+
# The order method returns an OrderedDict
33+
print Dependencies(my_items).order()
34+
```
35+
36+
```
37+
>>> OrderedDict([('B', []),
38+
>>> ('F', []),
39+
>>> ('E', ['F']),
40+
>>> ('D', ['B', 'E']),
41+
>>> ('C', ['D']),
42+
>>> ('A', ['B', 'C', 'D']),
43+
>>> ('Z', ['A', 'B', 'C', 'D'])])
44+
```
45+
46+
We can also output a list of the item names in order
47+
48+
```python
49+
print Dependencies(my_items).order_list()
50+
```
51+
52+
```
53+
>>> ['B', 'F', 'E', 'D', 'C', 'A', 'Z']
54+
```
55+
56+
57+
## Files
58+
59+
- `dependency_algo.ipynb` Jupyter Notebook where I walk through the steps I used in creating the algorithm
60+
- `Dependencies.py` contains the `Dependencies` class used to order a dictionary of items and their dependencies
61+
- `tests.py` has a few quick tests to check to make sure that the `Dependencies` class is functioning correctly

0 commit comments

Comments
 (0)