【问题标题】:Python: Combine several nested lists into a dictionaryPython:将几个嵌套列表组合成一个字典
【发布时间】:2011-07-31 07:32:20
【问题描述】:

我有一堆类似以下两个的列表:

['a', ['b', ['x', '1'], ['y', '2']]]
['a', ['c', ['xx', '4'], ['gg', ['m', '3']]]]

将所有这些组合成一个字典的最简单方法是:

{'a': {
    'b': {
        'x':1,
        'y':2
    }
    'c': {
        'xx':4,
        'gg': {
            'm':3
        }
    }
}

嵌套的深度是可变的。

【问题讨论】:

    标签: python list dictionary nested


    【解决方案1】:

    将这个问题分成两部分对我来说是最有意义的(好吧,那是我第一次读错了这个问题......'S)

    转换

    第一部分将[key, list1, list2]数据结构转换为嵌套字典:

    def recdict(elements):
        """Create recursive dictionaries from [k, v1, v2, ...] lists.
    
        >>> import pprint, functools
        >>> pprint = functools.partial(pprint.pprint, width=2)
        >>> pprint(recdict(['a', ['b', ['x', '1'], ['y', '2']]]))
        {'a': {'b': {'x': '1',
                     'y': '2'}}}
        >>> pprint(recdict(['a', ['c', ['xx', '4'], ['gg', ['m', '3']]]]))
        {'a': {'c': {'gg': {'m': '3'},
                     'xx': '4'}}}
        """
    
        def rec(item):
            if isinstance(item[1], list):
                return [item[0], dict(rec(e) for e in item[1:])]
    
            return item
    
        return dict([rec(elements)])
    

    它预计

    • 每个列表至少有两个元素
    • 每个列表的第一个元素是一个键
    • 如果列表的第二个元素是列表,那么所有后续元素也是列表;这些被组合成一个字典。

    棘手的一点(至少对我而言)是意识到您必须从递归函数返回一个列表而不是字典。否则,您无法组合构成某些列表的第二个和第三个元素的并行列表。

    为了使它更普遍有用(即对元组和其他序列),我会改变

    if isinstance(item[1], list):
    

    if (isinstance(item[1], collections.Sequence)
        and not isinstance(item[1], basestring)):
    

    你也可以让它适用于任何可迭代的,但这需要一点重组。

    合并

    第二部分合并在两个给定数据结构上运行第一个例程所产生的字典。我认为这将递归地合并任意数量的没有冲突键的字典,尽管除了这个用例之外我没有真正测试它。

    def mergedicts(*dicts):
        """Recursively merge an arbitrary number of dictionaries.
        >>> import pprint
        >>> d1 = {'a': {'b': {'x': '1',
        ...                   'y': '2'}}}
        >>> d2 = {'a': {'c': {'gg': {'m': '3'},
        ...                   'xx': '4'}}}
        >>> pprint.pprint(mergedicts(d1, d2), width=2)
        {'a': {'b': {'x': '1',
                     'y': '2'},
               'c': {'gg': {'m': '3'},
                     'xx': '4'}}}
        """
    
        keys = set(k for d in dicts for k in d)
    
        def vals(key):
            """Returns all values for `key` in all `dicts`."""
            withkey = (d for d in dicts if d.has_key(key))
            return [d[key] for d in withkey]
    
        def recurse(*values):
            """Recurse if the values are dictionaries."""
            if isinstance(values[0], dict):
                return mergedicts(*values)
            if len(values) == 1:
                return values[0]
            raise TypeError("Multiple non-dictionary values for a key.")
    
        return dict((key, recurse(*vals(key))) for key in keys)
    

    【讨论】:

    • aaah,对论点的理解,但不是列表。我为这个想法跳舞太久了,我打败了自己,哈哈
    • @jon_darkstar:确实需要一些坚持。有趣的是,有时最短的函数会花费最多的时间来编写。
    • 哈哈。我尝试使用不存在的“字典理解”大约 100 次,并且非常快速/轻松地编写了一个简洁和正确的解决方案,除了将每个字典包装在一个多余的列表中。我只是没有像你一样连接点并使用dict 构造函数。干得好
    • @jon_darkstar:是的,据我所知,传递dict(key, value) 生成器表达式与使用字典推导几乎是一回事。但我仍在使用 Python 2.6,所以我还没有真正检查过它们。
    【解决方案2】:

    这不是真正的“pythonic”,但我看不出没有递归的好方法

    def listToDict(l):
        if type(l) != type([]): return l
        return {l[0] : listToDict(l[1])}
    

    【讨论】:

    • 你的答案更漂亮+1
    • 谢谢。我认为它对输入做出了一些假设,但只要它像 OP 所说的那样格式化就可以了
    • 大声笑,只是它不能正常工作。我要多玩一点
    【解决方案3】:

    这是一个非常粗略的实现,它不能处理奇怪的情况,例如具有少于两个元素的列表,它会覆盖重复的键,但它可以让你开始:

    l1 = ['a', ['b', ['x', '1'], ['y', '2']]]
    l2 = ['a', ['c', ['xx', '4'], ['gg', ['m', '3']]]]
    
    def combine(d, l):
        if not l[0] in d:
            d[l[0]] = {}
    
        for v in l[1:]:
            if type(v) == list:
                combine(d[l[0]],v)
            else:
                d[l[0]] = v
    
    h = {}
    combine(h, l1)
    combine(h, l2)
    print h
    

    输出:

    {'a': {'c': {'gg': {'m': '3'}, 'xx': '4'}, 'b': {'y': '2', 'x': '1'}}}
    

    【讨论】:

    • 非常棒!它也有效。我需要坐下来看看它是如何工作的。关于输入的好消息是列表总是有 2 个元素和唯一键。我刚刚在 100 多个 10 个级别的列表上对其进行了测试,并且运行良好。
    猜你喜欢
    • 2020-07-29
    • 1970-01-01
    • 1970-01-01
    • 2018-10-18
    • 1970-01-01
    • 2019-04-06
    • 1970-01-01
    • 2021-06-08
    • 2018-10-05
    相关资源
    最近更新 更多