How can I form a flat ordered list from a set of elements, each of which may have requirements that they appear before and / or after some other element in the list?
Sample input
-----------------------
3: before 5 and after 2
8: before 5
2: before 3
5: no constraint
Sample output
-------------
[2, 3, 8, 5]
Obviously, the general solution to this problem is not unique (consider the case of many elements without restrictions), which is excellent - any result that satisfies the restrictions will be satisfied.
I also know that there are a lot of errors (duplicate elements, two elements that both want to be in front of each other, etc.). Right now I'm interested in the “happy path” - I will add error handling later.
Here are some test cases in Python. They are far from comprehensive, but this should be enough to give you an idea:
def test_some_unconstrained_elements():
l = ListBuilder()
l.add(1, after=None, before=None)
l.add(7, after=None, before=None)
assert set(l.to_list()) == {1, 7}
def test_element_which_should_appear_after_already_added_element():
l = ListBuilder()
l.add(5, after=None, before=None)
l.add(3, after=5, before=None)
assert l.to_list() == [5, 3]
def test_element_which_should_appear_after_element_added_later():
l = ListBuilder()
l.add(3, after=5, before=None)
l.add(5, after=None, before=None)
assert l.to_list() == [5, 3]
def test_element_which_should_appear_between_two_already_added_elements():
l = ListBuilder()
l.add(4, after=None, before=None)
l.add(2, after=None, before=None)
l.add(6, after=2, before=4)
assert l.to_list() == [2, 6, 4]
def test_two_elements_either_side_of_new_element():
l = ListBuilder()
l.add(4, after=6, before=None)
l.add(2, after=None, before=6)
l.add(6, after=None, before=None)
assert l.to_list() == [2, 6, 4]
def test_element_which_should_appear_after_missing_element():
l = ListBuilder()
l.add(4, after=6, before=None)
assert l.to_list() == [4]
def test_two_elements_which_should_appear_after_the_same_element():
l = ListBuilder()
l.add(4, after=None, before=None)
l.add(6, after=4, before=None)
l.add(8, after=4, before=None)
assert l[0] == 4
assert set(l[1:]) == {6, 8}
def test_fully_constrained_short_list():
l = ListBuilder()
l.add(3, after=4, before=None)
l.add(4, after=5, before=3)
l.add(5, after=None, before=4)
assert l.to_list() == [5, 4, 3]
NB. . , ( ; , ), : (