创建动态嵌套字典

时间:2014-12-19 05:45:29

标签: python dictionary nested

我有文件 abc.txt

abc/pqr/lmn/xyz:pass
abc/pqr/lmn/bcd:pass

我需要解析这些语句,输出应该在嵌套字典中,如下所示

{'abc':{'pqr':{'lmn':{'xyz':{'pass':1},{'bcd':{'pass':1}}}}}}

其中1是通过计数。 我能够做到这一点

import re
d={}
p=re.compile('[a-zA-z]+')
for line in open('abc.txt'):
    for key in p.findall(line):
        d['key']={}

我是python的新手,所以任何人都可以帮助我完成这个

3 个答案:

答案 0 :(得分:3)

这是我的答案的更新版本,其中树数据结构的叶子现在与其余部分的叶子不同。而不是树严格地是dict - 嵌套 - dict s,"离开"现在,每个分支上都有一个名为collections.Counterdict的不同子类的实例,这些子类可用于计算每个键发生的次数。我之所以这样做,是因为你回答了我的问题,如果每一行的最后一部分不是":pass",那将会发生什么(这是"我们必须为该键添加新计数")

嵌套字典通常称为 Tree 数据结构,可以递归定义 - 根是字典,也可以是字典。以下使用dict子类而不是普通dict,因为它使构造它们更容易,因为您不需要特殊情况下创建下一级的第一个分支(除了我仍然在添加" leaves"时会这样做,因为它们是不同的子类collections.Counter)。

from collections import Counter
import re

# (optional) trick that redefines Counter subclass to print like a regular dict
class Counter(Counter):
    def __repr__(self):
        return dict(self).__repr__()

# borrowed from answer @ http://stackoverflow.com/a/19829714/355230
class Tree(dict):
    def __missing__(self, key):
        value = self[key] = type(self)()
        return value

# some functions based on answer @ http://stackoverflow.com/a/14692747/355230
def nested_dict_get(nested_dict, keys):
    return reduce(lambda d, k: d[k], keys, nested_dict)

def nested_dict_set(nested_dict, keys, value):
    nested_dict_get(nested_dict, keys[:-1])[keys[-1]] = value

def nested_dict_update_count(nested_dict, keys):
    if nested_dict_get(nested_dict, keys[:-1]):  # update existing Counter
        nested_dict_get(nested_dict, keys[:-1]).update([keys[-1]])
    else:                                        # create a new  Counter
        nested_dict_set(nested_dict, keys[:-1], Counter([keys[-1]]))

d = Tree()
pat = re.compile(r'[a-zA-z]+')
with open('abc.txt') as file:
    for line in file:
        nested_dict_update_count(d, [w for w in pat.findall(line.rstrip())])

print d  # prints like a regular dict

为了测试修订代码的叶子计数功能,我使用了以下测试文件,该文件包含两次相同的行,一次以:pass结束,另一次以:fail结尾。

扩展abc.txt测试文件:

abc/pqr/lmn/xyz:pass
abc/pqr/lmn/bcd:pass
abc/pqr/lmn/xyz:fail
abc/pqr/lmn/xyz:pass

输出:

{'abc': {'pqr': {'lmn': {'bcd': {'pass': 1}, 'xyz': {'fail': 1, 'pass': 2}}}}}

如果您对有关计算每行的最后一个字的评论的正确解释,请告诉我。

答案 1 :(得分:1)

查看字典上的setdefault方法。

d = {}
d.setdefault('pqr', {}).setdefault('lmn', {}).setdefault('xyz', {})['pass'] = 1
d.setdefault('pqr', {}).setdefault('lmn', {}).setdefault('bcd', {})['pass'] = 1
d

给出

{'pqr': {'lmn': {'bcd': {'pass': 1}, 'xyz': {'pass': 1}}}}

答案 2 :(得分:0)

如果我理解你的问题:

sources = ["abc/pqr/lmn/xyz:pass", "abc/pqr/lmn/bcd:pass", "abc/pqr/lmn/xyz:pass"]


def prepare_source(source):
    path, value = source.split(':')
    elements = path.split('/')
    return elements, value


def add_key(elements, value):
    result = dict()
    if len(elements) > 1:
        result[elements[0]] = add_key(elements[1:], value)

    else:
        result[elements[0]] = {value: 1}

    return result


# base merge function get from here:
# http://stackoverflow.com/questions/7204805/dictionaries-of-dictionaries-merge
def merge(a, b, path=None):
    "merges b into a"
    if path is None: path = []
    for key in b:
        if key in a:
            if isinstance(a[key], dict) and isinstance(b[key], dict):
                merge(a[key], b[key], path + [str(key)])
            elif isinstance(a[key], int) and isinstance(b[key], int):
                a[key] += b[key]
            else:
                raise Exception('Conflict at %s' % '.'.join(path + [str(key)]))
        else:
            a[key] = b[key]
    return a


result = dict()

for source in sources:
    result = merge(result, add_key(*prepare_source(source)))

print result

输出将是:

{'abc': {'pqr': {'lmn': {'bcd': {'pass': 1}, 'xyz': {'pass': 2}}}}}