将列表字典拆分为字典列表

时间:2009-11-22 22:11:56

标签: python

我需要做的是转换这样的东西

{'key1': [1, 2, 3], 'key2': [4, 5, 6]}

[{'key1': 1, 'key2': 4}, {'key1': 2, 'key2': 5}, {'key1': 3, 'key2': 6}]

值列表的长度可能会有所不同! 最快的方法是什么(最好没有for循环)?

8 个答案:

答案 0 :(得分:11)

适用于任意数量的键

>>> map(dict, zip(*[[(k, v) for v in value] for k, value in d.items()]))
[{'key2': 4, 'key1': 1}, {'key2': 5, 'key1': 2}, {'key2': 6, 'key1': 3}]

例如:

d = {'key3': [7, 8, 9], 'key2': [4, 5, 6], 'key1': [1, 2, 3]}

>>> map(dict, zip(*[[(k, v) for v in value] for k, value in d.items()]))
[{'key3': 7, 'key2': 4, 'key1': 1}, {'key3': 8, 'key2': 5, 'key1': 2}, {'key3': 9, 'key2': 6, 'key1': 3}]

适用于任意数量的值或键的通用解决方案:(python2.6)

>>> from itertools import izip_longest
>>> d = {'key2': [3, 4, 5, 6], 'key1': [1, 2]}
>>> map(lambda a: dict(filter(None, a)), izip_longest(*[[(k, v) for v in value] for k, value in d.items()]))
[{'key2': 3, 'key1': 1}, {'key2': 4, 'key1': 2}, {'key2': 5}, {'key2': 6}]

如果您没有python2.6:

>>> d = {'key2': [3, 4, 5, 6], 'key1': [1, 2]}
>>> map(lambda a: dict(filter(None, a)), map(None, *[[(k, v) for v in value] for k, value in d.items()]))
[{'key2': 3, 'key1': 1}, {'key2': 4, 'key1': 2}, {'key2': 5}, {'key2': 6}]

答案 1 :(得分:5)

假设键的数量和每个键的值都是任意的并且是先验未知的,当然最简单的方法是使用for循环获得结果:

  itit = thedict.iteritems()
  k, vs = next(itit)
  result = [{k: v} for v in vs]
  for k, vs in itit:
    for d, v in itertools.izip(result, vs):
      d[k] = v

它可能会崩溃,但我对这样做的性能影响持怀疑态度(如果所涉及的数据结构非常庞大以至于需要进行性能优化,那么在内存中构建任何超出严格要求的额外辅助结构可能会变得昂贵 - 我的这种简单方法是特别小心避免任何这样的中间结构。)

编辑:另一种选择,如果整体数据结构很大,特别有趣,但在某些用例中你可能只需要“转换”结构的“点点滴滴”,就是建立一个类它提供了你需要的界面,但是“动态”,而不是“大爆炸”,“一劳永逸”的转换(如果原始结构可以改变并且转换后的需要改变,这可能特别有用反映原作的现状等等。)

当然,出于这样的目的,确定下游代码将使用的“词典列表”的确切功能非常有用。例如,假设你所需要的只是“只读”索引(不改变,迭代,切片,排序......):X[x]必须返回一个字典,其中每个键k映射到一个值,使得(caling O原始列表字典)X[x][k] is O[k][x]。然后:

class Wrap1(object):
  def __init__(self, O):
    self.O = O
  def __getitem__(self, x):
    return dict((k, vs[x]) for k, vs in self.O.iteritems())

如果你实际上不需要包装结构来跟踪对原始结构的修改,那么__getitem__也可能“缓存”它返回的字典:

class Wrap2(object):
  def __init__(self, O):
    self.O = O
    self.cache = {}
  def __getitem__(self, x):
    r = self.cache.get(x)
    if r is None:
      r = self.cache[x] = dict((k, vs[x]) for k, vs in self.O.iteritems())
    return r

请注意,此方法最终可能会在缓存中出现一些重复,例如,如果O的列表各有7个项目,则x==6x==-1的缓存可能会结束有两个相同的词组;如果这是一个问题,您可以在x中通过向__getitem__添加len(self.O)来标准化__iter__,然后再继续。

如果你还需要迭代,以及这个简单的索引,那就不太难了:只需添加一个 def __iter__(self, x): for i in xrange(len(self.O)): yield self[i] 方法,可以轻松实现,例如作为一个简单的发电机......:

__iter__

依此类推,逐步增加,如果您需要越来越多的列表功能(最糟糕的是,一旦您实现了这个self.L = list(self),您就可以构建self.L - 恢复到“大爆炸“方法 - 并且,对于任何进一步的请求,向self.__class__ = list; self[:] = self.L发展...但如果你想采用特殊方法的方法,你将不得不制作一个特殊的元类同样,或使用一些微妙的技巧,例如del,然后是适当的{{1}} s; - )。

答案 2 :(得分:2)

如果总有两个键可以使用:

[{'key1':a, 'key2':b} for (a,b) in zip(d['key1'], d['key2'])]

答案 3 :(得分:1)

>>> a = {'key1': [1, 2, 3], 'key2': [4, 5, 6]}
>>> [dict((key, a[key][i]) for key in a.keys()) for i in range(len(a.values()[0]))]
[{'key2': 4, 'key1': 1}, {'key2': 5, 'key1': 2}, {'key2': 6, 'key1': 3}]

答案 4 :(得分:1)

d = {'key1': [1, 2, 3], 'key2': [4, 5, 6]}

keys = d.keys()
vals = zip(*[d[k] for k in keys])
l = [dict(zip(keys, v)) for v in vals]
print l

产生

[{'key2': 4, 'key1': 1}, {'key2': 5, 'key1': 2}, {'key2': 6, 'key1': 3}]

答案 5 :(得分:1)

list(map( dict, zip(*([(key, val) for val in data[key]] for key in data.keys()))))

答案 6 :(得分:1)

没有for循环,地图的内部流程实际上是迭代的,只是没有关键字for

>>> x={'key1': [1, 2, 3], 'key2': [4, 5, 6]}

>>> map(lambda x,y:{'key1':x,'key2':y},x['key1'],x['key2'])

[{'key2': 4, 'key1': 1}, {'key2': 5, 'key1': 2}, {'key2': 6, 'key1': 3}]

答案 7 :(得分:0)

怎么样?

d = {'key1': [1, 2, 3], 'key2': [4, 5, 6]}
[dict(zip(d.keys(),i)) for i in zip(*d.values())]

返回:

[{'key1': 1, 'key2': 4}, {'key1': 2, 'key2': 5}, {'key1': 3, 'key2': 6}]