Python:将多个嵌套列表合并为字典
我有一堆列表,像下面这两个:
['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
}
}
}
这个字典的嵌套层数是可以变化的。
3 个回答
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)
1
这其实不是很“符合Python风格”,但我看不出有什么好的办法可以做到这一点而不使用递归。
def listToDict(l):
if type(l) != type([]): return l
return {l[0] : listToDict(l[1])}
2
这里有一个非常简单的实现方式,它没有处理一些特殊情况,比如列表里元素少于两个的情况,而且如果有重复的键,它会覆盖掉之前的键。不过这可以作为一个入门的参考:
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'}}}