5

Here is the input:

list_child_parent= [
    #first value is child, second is parent
    (0, 1),
    (1, 3),
    (8, 7),
    (3, 6),
    (4, 3),
    (5, 3)
]

The output needs to create a nested dictionary tree using these values. The tree will never be more than 6 levels deep.

For example:

output_dict = {
    6: {3: {1: {0: {}}, 4: {}, 5: {}}}, 7: {8: {}}
}

I have spent two days trying to accomplish this. I have tried writing functions to find where the key is in the tree and then add the new key after it, but I cannot produce code that can continue more than 3 levels. It's baffling and I feel that there is probably a standard library that can do this.

My experience level is low.

14
  • According to your 'example output' 3 is the parent of 8. But that's not what your input describes. Further, isn't 8 supposed to be the child of 7 ? I'm puzzled. Commented Jul 27, 2015 at 0:58
  • Is there a pattern to the key: value pairs? Commented Jul 27, 2015 at 0:58
  • 3 is the parent of 1, 4, and 5. Yes 8 is the child of 7 and I fixed that. Obviously I don't have the code that produces the output yet so I wrote that dictionary by hand. Commented Jul 27, 2015 at 1:18
  • There is no pattern to the pairs. You should assume that the list of parent child lairs can be in any order. Commented Jul 27, 2015 at 1:20
  • 1
    Shouldn't your tree be {6: {3: {1: {0: {}}, 4: {}, 5: {}}}, 7: {8: {}}}? You show 3 has childeren 1, 5, 4 in the tuples, but the output makes it seem like 1 has childs 5, 4, 0. Commented Jul 27, 2015 at 3:05

3 Answers 3

4

Not pretty and probably not Pythonic, but it should get you going:

#!/usr/bin/env python3

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] = {}
        all_items[parent][child] = all_items[child]
        has_parent.add(child)

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

if __name__ == '__main__':
    list_child_parent = [
        #first value is child, second is parent
        (0, 1),
        (1, 3),
        (8, 7),
        (3, 6),
        (4, 3),
        (5, 3)
    ]

    actual = make_map(list_child_parent)

    expected = {
        6: {
            3: {
                1: {
                    0: {}
                },
                4: {},
                5: {}
            }
        },
        7: {
            8: {}
        }
    }
    print('OK' if expected == actual else 'FAIL')
Sign up to request clarification or add additional context in comments.

3 Comments

This solution worked perfectly. It condensed my 45 lines of code that didn't work into a simple solution that is easy to understand. I have never seen set() used in that manner before, and your usage would not be obvious after reading the python docs. I should note, that once the tree is built, it is saved in cache and so performance is not an issue. However there are several thousand parent/child relationships and periodically the tree needs to be rebuilt as new data is added. This will probably run as cron for a flask website that I'm building.
Can you please help with the reverse i.e. making parent child pairs from the same nested dictionary?
@user3156040 The answer probably won't fit in a comment. Could you please create a new question and then post the link here?
2

This code will convert a tree from the given format into a tree structured dictionary. It's a lot of fluf, but it helps to keep track of what is happening. Performance wise it's pretty good.

LIST_CHILD_PARENTS = [                                                     
#first value is child, second is parent                                    
(0, 1),                                                                    
(1, 3),                                                                    
(8, 7),                                                                    
(3, 6),                                                                    
(4, 3),                                                                    
(5, 3)                                                                     
]                                                                          


class Node(object):                                                        
    def __init__(self, value):                    
        self.value = value
        # List of references to Node()'s.                                                
        self.child = []              
        # Reference to parent Node()                                                                           
        self.parent = None                                               
    def set_parent(self, parent):                                          
        self.parent = parent                                               
    def set_child(self, child):                                            
        self.child.append(child)                                           


def get_a_root(items):                                                     
    """Find a root node from items.                                        

    Grab some node and follow the parent pointers to a root.               
    """                                                                    
    cur_key = list(items.keys())[0]                                              
    while items[cur_key].parent is not None:                               
        cur_key = items[cur_key].parent.value                              
    parent = items[cur_key]                                                
    return parent                                                          

def extract_tree(items, root):                                             
    """Remove the tree from root in items.                                 
    """                                                                    
    cur_key = root.value                                                   
    this_node = items[cur_key]                                             
    if len(this_node.child) == 0:                                          
        items.pop(cur_key)                                                 
        return                                                             
    else:                                                                  
        for child in this_node.child:                                      
            extract_tree(items, child)                                     
        items.pop(cur_key)                                                  

def insert_from_root(tree, root):                                          
    """Insert the dictionary items from a tree.                            
    """                                                                    
    current = root                                                         
    if len(current.child) == 0:                                            
        tree[current.value] = {}                                           
        return                                                             
    else:                                                                  
        table = {}                                                         
        for child in current.child:                                        
            insert_from_root(table, child)                                 
        tree[current.value] = table                                                                                                

def build_graphs():                                                        
    """Map all input graphs into Node(object)'s.           

    Return: A hash table by value: Node(value, child, parent)              
    """                                                                    
    items = {}                                                             
    for child, parent in LIST_CHILD_PARENTS:                               
        if not child in items:                                       
            c_n = Node(child)  
            items[child] = c_n                 
        else:                                                              
            c_n = items[child]                                             
        if not parent in items:                                      
            p_n = Node(parent) 
            items[parent] = p_n                    
        else:                                                              
            p_n = items[parent]                                            
        p_n.set_child(c_n)                                                 
        c_n.set_parent(p_n)                                                                                       
    return items                                                           

def run_dict_builder():                                                    
    """Map the graphs from input and map into a dict.                                  

    Sequence:                                                              
        1- Map all graphs from input trees: list(tuple)             
        2- For each root node:                                             
            2A - Get a root node.                                      
            2B - Extract tree under this root from graphs list.                  
            2C - Insert the tree from this root into dict.                      
        3- Return the Dictionary Tree structure.                                                     
    """                                                                    
    graphs = build_graphs()                                                

    h_table = {}                                                           
    while len(graphs) > 0:                                                 
        root = get_a_root(graphs)                                          
        extract_tree(graphs, root)                                
        insert_from_root(h_table, root)                          
    return h_table                                                         

print(run_dict_builder())

2 Comments

Matt, thank you for contributing a very fine solution. This worked, but I need to use it in a python 3 environment (should have specified - sorry!). How would I change items.has_key on line 67 to make it work with python 3? AttributeError: 'dict' object has no attribute 'has_key'
@DanSafee You could use: if parent in items: instead. if key in dict:
0

Even though this question was answered a long time ago I wanted to give another solution that, to me, seems quite a bit cleaner. I mostly use this with lists of parents and children that get zipped together. For example

parents = [None, 1, 2, 3, 3, 2, 6, 6, 1, 9, 10, 10, 9, 13, 13]
children = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]

# {1: {2: {3: {4: {}, 5: {}}, 6: {7: {}, 8: {}}}, 9: {10: {11: {}, 12: {}}, 13: {14: {}, 15: {}}}}}

Implementation:

def create_tree(node_map, root=None):
""" Given a list of tuples (child, parent) return the nested dictionary representation.
"""

def traverse(parent, node_map, seen):
    children = {}
    for edge in node_map:
        if edge[1] == parent and edge[0] not in seen:
            seen.add(edge[0])
            children[edge[0]] = traverse(edge[0], node_map, seen)
    return children

return traverse(root, node_map, {root})

Usage:

Example of root the exclusion.

We want the root to be "a" but is excluded because a root should be indicated as having no parent (None).

parents = ["a", "b", "c", "a", "b", "e", "e"]
children = ["b", "c", "d", "e", "f", "g", "h"]
edges = list(zip(children, parents))
test_tree = create_tree(edges, "a")
print(test_tree)
# result {'b': {'c': {'d': {}}, 'f': {}}, 'e': {'g': {}, 'h': {}}}

Adding the root with no parent designates the node as the root.

parents = [None, "a", "b", "c", "a", "b", "e", "e"]
children = ["a", "b", "c", "d", "e", "f", "g", "h"]
edges = list(zip(children, parents))
test_tree = create_tree(edges, None)
print(test_tree)
# result: {'a': {'b': {'c': {'d': {}}, 'f': {}}, 'e': {'g': {}, 'h': {}}}}

Multiple trees can be represented by having multiple roots.

parents = [None, "a", "b", "c", None, "b", "e", "e"]
children = ["a", "b", "c", "d", "e", "f", "g", "h"]
edges = list(zip(children, parents))
test_tree = create_tree(edges, None)
print(test_tree)
# result: {'a': {'b': {'c': {'d': {}}, 'f': {}}}, 'e': {'g': {}, 'h': {}}}

To get the desired output from the original post we find and designate the roots.

edges = [
    #first value is child, second is parent
    (0, 1),
    (1, 3),
    (8, 7),
    (3, 6),
    (4, 3),
    (5, 3)
]
roots = {edge[1] for edge in edges} - {edge[0] for edge in edges}
edges += [(root, None) for root in roots]
test_tree = create_tree(edges, None)
print(test_tree)
# result: {6: {3: {1: {0: {}}, 4: {}, 5: {}}}, 7: {8: {}}}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.