递归结合字典

时间:2019-03-19 08:44:27

标签: python python-3.x recursion

好的,这是我的头脑。我有两个带有对象组的字典,如下所示:

groups = {
    'servers': ['unix_servers', 'windows_servers'],
    'unix_servers': ['server_a', 'server_b', 'server_group'],
    'windows_servers': ['server_c', 'server_d'],
    'server_group': ['server_e', 'server_f']
}

hosts = {
    'server_a': '10.0.0.1',
    'server_b': '10.0.0.2',
    'server_c': '10.0.0.3',
    'server_d': '10.0.0.4',
    'server_e': '10.0.0.5',
    'server_f': '10.0.0.6'
}

我正在寻找的输出是:

d3 = {
    'servers': {
        'unix_servers': {
            'server_a': '10.0.0.1',
            'server_b': '10.0.0.2',
            'server_group': {
                'server_e': '10.0.0.5',
                'server_f': '10.0.0.6'
            }
        },
        'windows_servers': {
            'server_c': '10.0.0.3',
            'server_d': '10.0.0.4'
        }
    }
}

我遇到的问题是我事先不知道有多少递归级别,因为嵌套组在理论上可以无限进行。另外,我在确定哪些键应该是组合字典中的顶级键时遇到了麻烦。

我目前有以下内容:

def resolve(d1, d2):
for k, v in d1.items():
    for i in v:
        if i in d2.keys():
            d3[k] = {i: d2[i]}

这将返回:

{
  "servers": {
    "unix_servers": {
      "server_a": "10.0.0.1",
      "server_b": "10.0.0.2",
      "server_group": {
        "server_e": "10.0.0.5",
        "server_f": "10.0.0.6"
      }
    },
    "windows_servers": {
      "server_c": "10.0.0.3",
      "server_d": "10.0.0.4"
    }
  },
  "unix_servers": {
    "server_b": "10.0.0.2"
  },
  "windows_servers": {
    "server_d": "10.0.0.4"
  },
  "server_group": {
    "server_f": "10.0.0.6"
  }
}

这很接近,但是显然缺少递归并且不处理键的嵌套。主要是在这里寻找指针,递归逻辑还不适合我...

6 个答案:

答案 0 :(得分:3)

我认为这可以满足您的要求

def resolve(groups, hosts):
    # Groups that have already been resolved
    resolved_groups = {}
    # Group names that are not root
    non_root = set()
    # Make dict with resolution of each group
    result = {}
    for name in groups:
        result[name] = _resolve_rec(name, groups, hosts, resolved_groups, non_root)
    # Remove groups that are not root
    for name in groups:
        if name in non_root:
            del result[name]
    return result

def _resolve_rec(name, groups, hosts, resolved_groups, non_root):
    # If group has already been resolved finish
    if name in resolved_groups:
        return resolved_groups[name]
    # If it is a host finish
    if name in hosts:
        return hosts[name]
    # New group resolution
    resolved = {}
    for child in groups[name]:
        # Resolve each child
        resolved[child] = _resolve_rec(child, groups, hosts, resolved_groups, non_root)
        # Mark child as non-root
        non_root.add(child)
    # Save to resolved groups
    resolved_groups[name] = resolved
    return resolved

以您的示例为例:

groups = {
    'servers': ['unix_servers', 'windows_servers'],
    'unix_servers': ['server_a', 'server_b', 'server_group'],
    'windows_servers': ['server_c', 'server_d'],
    'server_group': ['server_e', 'server_f']
}

hosts = {
    'server_a': '10.0.0.1',
    'server_b': '10.0.0.2',
    'server_c': '10.0.0.3',
    'server_d': '10.0.0.4',
    'server_e': '10.0.0.5',
    'server_f': '10.0.0.6'
}

d3 = {
    'servers': {
        'unix_servers': {
            'server_a': '10.0.0.1',
            'server_b': '10.0.0.2',
            'server_group': {
                'server_e': '10.0.0.5',
                'server_f': '10.0.0.6'
            }
        },
        'windows_servers': {
            'server_c': '10.0.0.3',
            'server_d': '10.0.0.4'
        }
    }
}


print(resolve(groups, hosts) == d3)
# True

请注意,如果您的组A包含组B,但组B包含组A,则对于格式错误的输入,这可能会无限递归。 >

答案 1 :(得分:1)

假设您可以使用交叉引用数据结构,则不必在此处使用递归。

from itertools import chain

group_dicts = {k: {} for k in groups}

for g in group_dicts:
    for child_key in groups[g]:
        child = group_dicts.get(child_key, hosts.get(child_key))
        group_dicts[g][child_key] = child

# remove entries that are referenced at least once
not_top_levels = set(chain.from_iterable(groups.values()))
result = {g: group_dicts[g] for g in group_dicts if g not in not_top_levels}

与其他解决方案不同,这将正确处理循环和无限递归的组,因为所有dict引用都是共享的。当您的groups拓扑描述一棵树时,这将与递归解决方案完全相同。但是,如果您的groups在拓扑上描述了有向无环图,则此解决方案将共享出现多次的所有节点的字典,而递归解决方案会将副本复制并扩展到规则树中,这将不会如果您不需要更改命令,那真的不是问题。如果您的groups在拓扑上描述了带有循环的图,则将创建这些循环,而递归解将由于无限递归而下降。

答案 2 :(得分:1)

您可以使用简单的递归:

def build(val): 
  return {i:build(i) for i in groups[val]} if val in groups else hosts[val]

import json
print(json.dumps({'servers':build('servers')}, indent=4))

输出:

{
  "servers": {
    "unix_servers": {
        "server_a": "10.0.0.1",
        "server_b": "10.0.0.2",
        "server_group": {
            "server_e": "10.0.0.5",
            "server_f": "10.0.0.6"
        }
    },
    "windows_servers": {
        "server_c": "10.0.0.3",
        "server_d": "10.0.0.4"
     }
  }
}

答案 3 :(得分:0)

<div class="container">
  <div class="block">

  </div>
</div>

这将给出

d3={}
main={}
for i in groups['servers']:
    if(i in groups):
        d={}
        for j in groups[i]:
            if(j in groups):
                dd={}
                for k in groups[j]:
                    dd[k]=hosts[k]
                d[j]=dd
            else:
                d[j]=hosts[j]
        main[i]=d
d3['servers']=main
print(d3)

答案 4 :(得分:0)

我想它只需要两个循环,就不再需要。在这里,dict组将被更新,更确切地说,它将丢失一些键值。

groups = {k: {s: None for s in subs} for k, subs in groups.items()}                                                                                                                                                        

for k, subs in groups.items():                                                                                                                                                                                        
    for subs_k, subs_v in subs.items():                                                                                                                                                                                       
        if subs_v is not None:                                                                                                                                                                                         
            continue                                                                                                                                                                                               
        if subs_k in groups:                                                                                                                                                                                           
            groups[k][subs_k] = groups[subs_k]                                                                                                                                                                             
            del groups[subs_k]                                                                                                                                                                                       
        else:                                                                                                                                                                                                      
            groups[k][subs_k] = hosts[subs_k]


print(groups)

您可能想使用defaultdict

对其进行重写

答案 5 :(得分:0)

这是python的新手,但我一直在为此苦苦挣扎,但终于找到了解决方案,尽管时间很长。

groups = {
    'servers': ['unix_servers', 'windows_servers'],
    'unix_servers': ['server_a', 'server_b', 'server_group'],
    'windows_servers': ['server_c', 'server_d'],
    'server_group': ['server_e', 'server_f']
}

hosts = {
    'server_a': '10.0.0.1',
    'server_b': '10.0.0.2',
    'server_c': '10.0.0.3',
    'server_d': '10.0.0.4',
    'server_e': '10.0.0.5',
    'server_f': '10.0.0.6'
}

result = {}

parent = '';

levels = [];

def check(s,r):
    if(r[s]==''): #check for each blank element in the result recursively
        sublist = {}
        for k in groups[s]: # check if the key exist in group if exist append children to result.   
            if k in hosts :
                sublist[k] = hosts[k] # check if host exist in hosts for this key
            else:
                sublist[k]=''
        if(key_exist(result, s)): # check if the key exist in result if exist append to result.             
            d = result 
            p = None
            for key in levels:
                p = d
                d = d[key]
            p[key] = sublist
            del levels[:]
        for x in sublist :
            if(sublist[x] == ''):
                check(x,p[key])

def resolve(r):
    for s in r:
        if(isinstance(r[s],dict)):
            return resolve(r[s])
        else:
            check(s,r)


def key_exist(groups, key): 
    for s in groups:
        if key in groups:
            levels.append(key);
            return True
        else:
            if(isinstance(groups[s],dict)):
                levels.append(s);
                return key_exist(groups[s], key)
    return False

# Find the root or parent element
for k in groups.keys(): 
    found = False
    for j in groups.keys(): 
        if k in groups[j]:
            found = False
        else:
            found = True
    if(found):
        parent = k  # root or parent element

# start making result with root element 
s = {}
for k in groups[parent]:
    s[k]='' # initialize child elements with blank
result[parent] = s 

resolve(result) 

print(result)
相关问题