Search code examples
pythondictionarytreeparent-child

Avoiding loops in dictionary generation from child-parent relationships


I am trying to create a dictionary tree from child-parent relathionships but the issue here is that the child can contain one of his predecesors generating loops that I would like to avoid.

list_child_parent = [(2,1),(3,2),(4,2),(5,3),(6,4),(2,6)]
def make_map(list_child_parent):
    has_parent = set()
    all_items = {}
    for child, parent in list_child_parent:
        if parent not in all_items:
            all_items[parent] = {}
        if child not in all_items:
            all_items[child] = {}
        
        if parent not in all_items[child]:
            all_items[parent][child] = all_items[child]
            has_parent.add(child)
        else:
            continue

    result = {}
    for key, value in all_items.items():
        if key not in has_parent:
            result[key] = value
    return result
make_map(list_child_parent)

The code works well if a son contains his father but not if it is the granfather...
I think another approach is needed.

The expected results could be any if it makes sense:
{1: {2: {3: {5: {}}, 4: {6: {}}}}}
{1: {2: {3: {5: {}}, 4: {6: 2}}}}
{1: {2: {3: {5: {}}, 4: {6: {2:{}}}}}}

The current result is:
{1: {2: {3: {5: {}}, 4: {6: {2: {...}}}}}}


Solution

  • You can use a set to keep track of all the children, so that you can make the dict of descendants an empty dict if the current child has previously been a child of another parent:

    def make_map(list_child_parent):
        mapping = {}
        children = set()
        for child, parent in list_child_parent:
            mapping.setdefault(parent, {})[child] = (
                {} if child in children
                else mapping.setdefault(child, {})
            )
            children.add(child)
        return {
            parent: mapping[parent]
            for parent in mapping.keys() - children
        }
    

    so that:

    make_map([(2, 1), (3, 2), (4, 2), (5, 3), (6, 4), (2, 6)])
    

    returns:

    {1: {2: {3: {5: {}}, 4: {6: {2: {}}}}}}
    

    Demo: Try it online!