【问题标题】:Python - Flatten the list of dictionariesPython - 展平字典列表
【发布时间】:2018-07-19 22:07:09
【问题描述】:

词典列表:

data = [{
         'a':{'l':'Apple',
                'b':'Milk',
                'd':'Meatball'},
         'b':{'favourite':'coke',
              'dislike':'juice'}
         },
         {
         'a':{'l':'Apple1',
                'b':'Milk1',
                'd':'Meatball2'},
         'b':{'favourite':'coke2',
              'dislike':'juice3'}
         }, ...
]

我需要加入所有嵌套字典以达到预期输出:

 [{'d': 'Meatball', 'b': 'Milk', 'l': 'Apple', 'dislike': 'juice', 'favourite': 'coke'},
  {'d': 'Meatball2', 'b': 'Milk1', 'l': 'Apple1', 'dislike': 'juice3', 'favourite': 'coke2'}]

我尝试嵌套列表理解,但无法将 dict 连接在一起:

L = [y for x in data for y in x.values()]
print (L)

[{'d': 'Meatball', 'b': 'Milk', 'l': 'Apple'}, 
 {'dislike': 'juice', 'favourite': 'coke'}, 
{'d': 'Meatball2', 'b': 'Milk1', 'l': 'Apple1'}, 
 {'dislike': 'juice3', 'favourite': 'coke2'}]

我正在寻找最快的解决方案。

【问题讨论】:

标签: python list dictionary join list-comprehension


【解决方案1】:

您可以使用itertools.chain

>>> from itertools import chain
# timeit: ~3.40
>>> [dict(chain(*map(dict.items, d.values()))) for d in data]
[{'l': 'Apple', 
  'b': 'Milk', 
  'd': 'Meatball', 
  'favourite': 'coke', 
  'dislike': 'juice'}, 
 {'l': 'Apple1', 
  'b': 'Milk1', 
  'dislike': 'juice3', 
  'favourite': 'coke2', 
  'd': 'Meatball2'}]

chainmap* 的使用使这个表达式成为以下双重嵌套理解的简写,它实际上在我的系统 (Python 3.5.2) 上执行得更好,而且不会更长:

# timeit: ~2.04
[{k: v for x in d.values() for k, v in x.items()} for d in data]
# Or, not using items, but lookup by key
# timeit: ~1.67
[{k: x[k] for x in d.values() for k in x} for d in data]

注意:

RoadRunner's 循环和更新方法在timeit: ~1.37 上优于这两种单行方法

【讨论】:

  • 我喜欢你的公平行为,因为添加了另一个更好的解决方案的时间,所以不要重新接受;)谢谢。
  • @jezrael 谢谢,不,不需要隐藏这个事实。比较这 3 种风格如此不同的方法很有趣,并看到简单的循环击败了理解,尤其是内置和 itertools 的厨房水槽:)
  • dict(chain(*map(... 的一个更易读的替代方案是[ChainMap(*d.values()) for d in data]。不过,它比其他方法慢。
  • 如果我想展平[{k: v for x in d.values() for k, v in x.items()} for d in data] 的输出,这是一个字典列表,它会给出错误TypeError: descriptor 'items' requires a 'dict' object but received a 'str'?这个结果就像输入数据一样。
【解决方案2】:

您可以使用 2 个嵌套循环和dict.update() 将内部字典添加到临时字典并将其添加到末尾:

L = []
for d in data:
    temp = {}
    for key in d:
        temp.update(d[key])

    L.append(temp)

# timeit ~1.4
print(L)

哪些输出:

[{'l': 'Apple', 'b': 'Milk', 'd': 'Meatball', 'favourite': 'coke', 'dislike': 'juice'}, {'l': 'Apple1', 'b': 'Milk1', 'd': 'Meatball2', 'favourite': 'coke2', 'dislike': 'juice3'}]

【讨论】:

    【解决方案3】:

    您可以使用functools.reduce 和一个简单的列表推导来展平字典列表

    >>> from functools import reduce 
    
    >>> data = [{'b': {'dislike': 'juice', 'favourite': 'coke'}, 'a': {'l': 'Apple', 'b': 'Milk', 'd': 'Meatball'}}, {'b': {'dislike': 'juice3', 'favourite': 'coke2'}, 'a': {'l': 'Apple1', 'b': 'Milk1', 'd': 'Meatball2'}}]
    >>> [reduce(lambda x,y: {**x,**y},d.values()) for d in data]
    >>> [{'dislike': 'juice', 'l': 'Apple', 'd': 'Meatball', 'b': 'Milk', 'favourite': 'coke'}, {'dislike': 'juice3', 'l': 'Apple1', 'd': 'Meatball2', 'b': 'Milk1', 'favourite': 'coke2'}]
    

    时间基准如下:

    >>> import timeit
    >>> setup = """
          from functools import reduce
          data = [{'b': {'dislike': 'juice', 'favourite': 'coke'}, 'a': {'l': 'Apple', 'b': 'Milk', 'd': 'Meatball'}}, {'b': {'dislike': 'juice3', 'favourite': 'coke2'}, 'a': {'l': 'Apple1', 'b': 'Milk1', 'd': 'Meatball2'}}]
      """
    >>> min(timeit.Timer("[reduce(lambda x,y: {**x,**y},d.values()) for d in data]",setup=setup).repeat(3,1000000))
    >>> 1.525032774952706
    

    我机器上其他答案的时间基准

    >>> setup = """
            data = [{'b': {'dislike': 'juice', 'favourite': 'coke'}, 'a': {'l': 'Apple', 'b': 'Milk', 'd': 'Meatball'}}, {'b': {'dislike': 'juice3', 'favourite': 'coke2'}, 'a': {'l': 'Apple1', 'b': 'Milk1', 'd': 'Meatball2'}}]
        """
    >>> min(timeit.Timer("[{k: v for x in d.values() for k, v in x.items()} for d in data]",setup=setup).repeat(3,1000000))
    >>> 2.2488374650129117
    
    >>> min(timeit.Timer("[{k: x[k] for x in d.values() for k in x} for d in data]",setup=setup).repeat(3,1000000))
    >>> 1.8990078769857064
    
    >>> code = """
          L = []
          for d in data:
              temp = {}
              for key in d:
                  temp.update(d[key])
    
              L.append(temp)
        """
    
    >>> min(timeit.Timer(code,setup=setup).repeat(3,1000000))
    >>> 1.4258553800173104
    
    >>> setup = """
          from itertools import chain
          data = [{'b': {'dislike': 'juice', 'favourite': 'coke'}, 'a': {'l': 'Apple', 'b': 'Milk', 'd': 'Meatball'}}, {'b': {'dislike': 'juice3', 'favourite': 'coke2'}, 'a': {'l': 'Apple1', 'b': 'Milk1', 'd': 'Meatball2'}}]
        """
    >>> min(timeit.Timer("[dict(chain(*map(dict.items, d.values()))) for d in data]",setup=setup).repeat(3,1000000))
    >>> 3.774383604992181
    

    【讨论】:

      【解决方案4】:

      如果您的嵌套字典只有“a”和“b”键,那么我建议使用以下解决方案,我发现它快速且易于理解(出于可读性目的):

      L = [x['a'] for x in data]
      b = [x['b'] for x in data]
      
      for i in range(len(L)):
          L[i].update(b[i])
      
      # timeit ~1.4
      
      print(L)
      

      【讨论】:

        猜你喜欢
        • 2019-11-04
        • 2021-09-07
        • 1970-01-01
        • 2021-01-05
        • 2019-01-11
        • 2019-05-03
        • 1970-01-01
        • 2019-05-09
        • 2014-02-26
        相关资源
        最近更新 更多