如何将三个列表压缩到嵌套字典中

时间:2018-12-20 14:55:27

标签: python list dictionary

我有三个列表:

z1 = ['A', 'A', 'B', 'B']
z2 = ['k1', 'k2', 'k1', 'k2']
z3 = ['v1', 'v2', 'v3', 'v4']

当我写的时候:

print(dict(zip(z2, z3)))

这是我的输出:

{'k2': 'v4', 'k1': 'v3'}

我希望这样:

{'A':{'k1': 'v1', 'k2': 'v2'} , 'B':{'k1': 'v3', 'k2': 'v4'}}

能否请您告诉我如何获得预期的结果?

3 个答案:

答案 0 :(得分:11)

函数zip()可以接受两个以上的可迭代对象。因此,您可以使用zip(z1, z2, z3)代替zip(z2, z3)。但是,您仍然需要对项目进行分组,因为仅包装dict()不能正常工作,因为它无法处理3元组所需的嵌套字典。

要正确地对项目进行分组,我将使用collections.defaultdict()

from collections import defaultdict

z1 = ['A', 'A', 'B', 'B']
z2 = ['k1', 'k2', 'k1', 'k2']
z3 = ['v1', 'v2', 'v3', 'v4']

d = defaultdict(dict)
for x, y, z in zip(z1, z2, z3):
    d[x][y] = z

print(d)
# defaultdict(<class 'dict'>, {'A': {'k1': 'v1', 'k2': 'v2'}, 'B': {'k1': 'v3', 'k2': 'v4'}})

以上方法之所以有效,是因为defaultdict(dict)为不存在的键初始化了一个字典。它为您处理密钥字典的创建。

此外,如果您用dict包装最终结果:

print(dict(d))
# {'A': {'k1': 'v1', 'k2': 'v2'}, 'B': {'k1': 'v3', 'k2': 'v4'}}

注意defaultdict只是dict的子类,因此您可以将其与普通词典一样对待。

答案 1 :(得分:3)

这是使用itertools.groupby的单行代码,但是除了是单个表达式之外,它实际上没有提供比RoadRunner提供的默认字典解决方案带来的任何好处。

>>> from itertools import groupby
>>> from operator import itemgetter
>>> keyf = itemgetter(0)
>>> dict((k, dict(v2 for _,v2 in v)) for k, v in groupby(zip(z1, zip(z2,z3)), key=keyf))
{'A': {'k2': 'v2', 'k1': 'v1'}, 'B': {'k2': 'v4', 'k1': 'v3'}}

这仅是因为它是如此之短,因为它利用了z1已经被排序的事实。如果不是,则需要使用相同的键函数对zip的输出进行排序,然后再将其传递到groupby

dict((k, dict(v2 for _,v2 in v))
       for k, v in groupby(sorted(zip(z1, zip(z2,z3)),
                                  key=keyf),
                           key=keyf))

破坏其工作原理...

  1. zip(z1, zip(z2, ze))为外部字典创建键值对:

    [('A', ('k1', 'v1')),
     ('A', ('k2', 'v2')),
     ('B', ('k1', 'v3')),
     ('B', ('k2', 'v4'))]
    
  2. groupby有效地将每个键(AB)与其元组配对:

    [('A', <itertools._grouper object at 0x100f656d0>),
     ('B', <itertools._grouper object at 0x100f655d0>)]
    

    每个_grouper是可迭代的,包含具有相同键的所有键/值对。

  3. dict(v2 for _,v2 in v)仅从_grouper中提取键/值对,而留下键,我们可以从{{1}返回的元组的第一个元素中获取键}。

答案 2 :(得分:3)

为了完整起见,您可以使用dict.setdefault,从而避免了导入,但这样做的代价是在每次迭代中创建和返回空字典都需要很小的开销。

d = {}
for x, y, z in zip(z1, z2, z3):
    d.setdefault(x,{})[y] = z

print(d)
# {'A': {'k1': 'v1', 'k2': 'v2'}, 'B': {'k1': 'v3', 'k2': 'v4'}}

另一种解决方案(不建议使用)使用itertools.groupby

d = {}
for k, g in groupby(enumerate(zip(z2, z3)), key=lambda x: z1[x[0]]):
    _, b = zip(*g)
    d[k] = dict(b)

print(d)
# {'A': {'k1': 'v1', 'k2': 'v2'}, 'B': {'k1': 'v3', 'k2': 'v4'}}