在另一个嵌套的dict列表中添加dict列表并检查父项

时间:2014-07-30 07:23:52

标签: python list dictionary yaml

我试图在dict的嵌套列表中追加一个字典,同时检查相同的父母,然后在适当的位置附加。我的输入dicts和所需输出例如:

输入

Dict1 = [{'name':'A', 
          'childs':[{
                'name':'B', 
                'childs':[{
                      'name':'C',
                       'childs':[{
                             'name':'D', 
                             'childs': None}]}]}]},
          {'name':'E', 'childs':None}]
Dict2=[{'name':'B',
        'childs':[{
              'name':'C',
              'childs':[{
                    'name':'X', 
                    'childs':None}]}]}]

输出: -

Dict1 = [{'name':'A', 
          'childs':[{
                'name':'B', 
                'childs':[{
                      'name':'C',
                       'childs':[{
                             'name':'D', 
                             'childs': None},
                            {'name':'X' , 
                             'childs':None}}]}]},
          {'name':'E' , 'childs':None}]

它只是附加一个字典作为另一个字典的孩子,但我无法弄清楚,如何在两个字典中迭代然后追加时检查相同的父母。

3 个答案:

答案 0 :(得分:1)

def get_dict_with_name(name, list_of_dicts):
     ''' 
     returns the dict that has the same name as name from list_of_dicts
     assured a dict or None
     '''
     if not list_of_dicts:
         return None

     for _dict in list_of_dicts:
         if name == _dict['name']:
             return _dict
         # check children as well
         dict_from_children = get_dict_with_name(name, _dict['childs'])
         if dict_from_children:
             return dict_from_children

     return None

 def append_stuff(list_of_dicts_1, list_of_dict_2):
     ''' 
     iter through all of list_of_dict_2, and merge with 1
     '''
     if not list_of_dict_2:
         return
     if not list_of_dicts_1:
         return

     for _dict in list_of_dict_2:
         name = _dict['name']
         dict_in_1 = get_dict_with_name(name, list_of_dicts_1)
         # if u dont get a dict, simply add it as a new dict to the list
         if not dict_in_1:
             list_of_dicts_1.append(_dict)
             continue

         # if you found, now check with children - each of these children in that dict
         append_stuff(dict_in_1['childs'], _dict['childs'])

将其添加到list_of_dicts_1。应该这样做。 但是,由于它具有这种层次结构,因此构建树结构对您来说会更有用。

在您的输入中,您调用了两个列表Dict1和Dict2 所以在这里,你调用append_stuff(Dict1,Dict2) 并且Dict1具有所需的输出。

答案 1 :(得分:0)

1。方法描述

我提出以下方法:

  • gen_base从包含childes和parent的给定字典列表生成数据库。数据库是一个帮助索引不同元素的字典。它包含{..., 'B': {'childs': ['C'], 'parent': 'A'}, ...}
  • 等元素
  • build_tree创建一个方法,使用生成的数据库构建给定名称的树。
  • merge_from_base合并数据库元素以形成完整的树。

以下是方法:

2。执行示例

Dict1Dict2成为您提供的词典列表,并Dict3所需的结果。

>>> base = gen_base(Dict1 + Dict2)
>>> base
{'A': {'childs': ['B'], 'parent': None},
 'B': {'childs': ['C'], 'parent': 'A'},
 'C': {'childs': ['D', 'X'], 'parent': 'B'},
 'D': {'childs': [], 'parent': 'C'},
 'E': {'childs': [], 'parent': None},
 'X': {'childs': [], 'parent': 'C'}}

>>> res = merge_from_base(base=base,)
>>> res
[{'name': 'A', 'childs': [
    {'name': 'B', 'childs': [
        {'name': 'C', 'childs': [
            {'name': 'D', 'childs': None,},
            {'name': 'X', 'childs': None,}],}],}],},
 {'name': 'E', 'childs': None,}]

>>> res == Dict3
True

3。来源

#
# 1. Generate a data base
#
def gen_base(lst, parent=None, res=None):
    """Generates a data-base with childes and parents"""
    if res is None: res = {}    # res will contain the required result
    if lst is None: return res  # Given list (in our case, it is Dict1 + Dict2)
    for d in lst:   # For each element d in the list ...
        if d['name'] in res:    # ... if the name exists in the data-base,
            if res[d['name']]['childs'] is None:    # if it has no child
                res[d['name']]['childs'] = []
            else:                                   # if there are children
                for c in d['childs']:               # get teir names in a list
                    if c['name'] not in res[d['name']]['childs']:
                        res[d['name']]['childs'].append(c['name'])
        else:                   # ... if the name does not exist in the base
            res[d['name']] = {  # ... create a new one
                'childs':[] if d['childs'] is None else [c['name'] for c in d['childs']],
                'parent':parent}
        # In any case, do the same for children (recursively)
        gen_base(d['childs'], parent=d['name'], res=res)
    return res

#
# 2. Build a tree from a root and a data-base
#
def build_tree(base, root):
    """Builds the tree of the given root using informations from the base"""
    ret = {}    # The result will contain a name and a list of childes
    ret['name'] = root  # The root is a string such as 'A', 'B', ...
    if base[root]['childs'] == []:
        ret['childs'] = None
        # If there are no children, the tree stops
    else:
        # # If there are children, the tree continues to grow recursively
        ret['childs'] = map(lambda e: build_tree(base, e), base[root]['childs'])
    return ret

#
# 3. Merge the data
#
def merge_from_base(base,):
    """Uses the base to get the roots (which 'parent' is None) and complete the trees"""
    ret = []
    for p in base:
        if base[p]['parent'] is None:   # The final list contains the roots
            # ... and the roots point to their children
            ret.append(build_tree(base=base, root=p))
    return ret

严重Taha,

答案 2 :(得分:0)

这是一个稍微修改过的Node类,用于构建相同的结构。我觉得核心逻辑在这里更容易理解。

$> cat input.txt ( From your sample input )
{'d2': [{'childs': [{'childs': [{'childs': None, 'name': 'X'}], 'name': 'C'}], 'name': 'B'}], 'd1': [{'childs': [{'childs': [{'childs': [{'childs': None, 'name': 'D'}], 'name': 'C'}], 'name': 'B'}], 'name': 'A'}, {'childs': None, 'name': 'E'}]}

$>vi test1.py (sorry for the name, your question was too long)
class Node(object):
    def __init__(self, name):
    ''' not making children None, [] is easier to work with '''
        self.name = name
        self.children = []

    def add_child(self, child):
        self.children.append(child)

    def flatten_to_dict(self):
        ''' to get it back to your format '''
        my_dict = {'name': self.name, 'childs': []}

        for child in self.children:
            my_dict['childs'].append(child.flatten_to_dict())

        return my_dict

    @staticmethod
    def from_dict(inp_dict):
        ''' taking the input as dictionaries - your format converter '''
        root = Node(inp_dict['name'])
        if not inp_dict['childs']:
            return root

        for child in inp_dict['childs']:
            root.add_child(Node.from_dict(child))
        return root

    def __repr__(self):
        ''' u know what this is for '''
        return "%s" % self.flatten_to_dict()

    def find_node_with_name(self, name):
        if self.name == name:
            return self

        for child in self.children:
            found_node = child.find_node_with_name(name)
            if found_node:
                return found_node
        return None

    @staticmethod
    def merge_nodes(node1, node2):
        ''' the actual core logic, very simple '''
        the_node = node1.find_node_with_name(node2.name)

        if not the_node:
            return None

        for child in node2.children:
            n = Node.merge_nodes(the_node, child)
            if not n:
                the_node.add_child(child)
        return the_node

    def merge_with_node(self, another_node):
        ''' want to return a new instance, not modify current node, so a wrapper
        for a static function, one time new instance, and then modify that in place '''

        # creating a new instance by deseriazling the serialized object
        # since i dont want this to happen recursively, hence the wrapper
        node_to_return = Node.from_dict(self.flatten_to_dict())
        x = Node.merge_nodes(node_to_return, another_node)
        return node_to_return


if __name__ == '__main__':
    ''' preprocessing, so we can work with the Node class'''
    data = eval(open('input.txt').read())
    d1 = data['d1'] # first list of data dicts
    d2 = data['d2'] # second list of data dicts
    # as per you're input example of Dict1 and Dict2
    Dict1 = map(Node.from_dict, d1)
    Dict2 = map(Node.from_dict, d2)

    # assuming Dict2 should just be one element, then Dict2[0]
    Dict2 = Dict2[0]
    result = []
    # try to merge with every dict in Dict1
    for _dict in Dict1:
        res = _dict.merge_with_node(Dict2)
        result.append(res)

    print result


$>python test1.py