簡體   English   中英

帶有嵌套字典的 Python 字典總和列表

[英]Python sum list of dicts by key with nested dicts

我有一個字典列表,並想設計一個函數來輸出一個新字典,其中包含列表中所有字典中每個唯一鍵的總和。

對於列表:

[
    {
         'apples': 1,
         'oranges': 1,
         'grapes': 2
    },
    {
         'apples': 3,
         'oranges': 5,
         'grapes': 8
    },
    {
         'apples': 13,
         'oranges': 21,
         'grapes': 34
    }
]

到目前為止一切順利,這可以通過計數器來完成:

def sumDicts(listToProcess):
    c = Counter()
    for entry in listToProcess:
        c.update(entry)
    return (dict(c))

哪個正確返回:

{'apples': 17, 'grapes': 44, 'oranges': 27}

當我列表中的字典開始包含嵌套字典時,問題就來了:

[
    {
        'fruits': {
            'apples': 1,
            'oranges': 1,
            'grapes': 2
            },
        'vegetables': {
            'carrots': 6,
            'beans': 3,
            'peas': 2
        },
        'grains': 4,
        'meats': 1  
    },
    {
        'fruits': {
            'apples': 3,
            'oranges': 5,
            'grapes': 8
            },
        'vegetables': {
            'carrots': 7,
            'beans': 4,
            'peas': 3
        },
        'grains': 3,
        'meats': 2  
    },
    {
        'fruits': {
            'apples': 13,
            'oranges': 21,
            'grapes': 34
            },
        'vegetables': {
            'carrots': 8,
            'beans': 5,
            'peas': 4
        },
        'grains': 2,
        'meats': 3
    },
]

現在同樣的函數會給出一個 TypeError 因為計數器不能添加兩個字典。

期望的結果是:

{
    'fruits': {
        'apples': 17,
        'oranges': 27,
        'grapes': 44
        },
    'vegetables': {
        'carrots': 21,
        'beans': 12,
        'peas': 9
    },
    'grains': 9,
    'meats': 6  
}

關於如何以相當有效的、Pythonic 的、可概括的方式做到這一點的任何想法?

我會通過對遞歸定義collections.defaultdict對象執行遞歸合並來做到這一點。

from collections import defaultdict

def merge(d, new_d):
    for k, v in new_d.items():
        if isinstance(v, dict):
            merge(d[k], v)
        else: 
            d[k] = d.setdefault(k, 0) + v

# https://stackoverflow.com/a/19189356/4909087    
nested = lambda: defaultdict(nested)
d = nested()

for subd in data:
    merge(d, subd)

使用default_to_regular將其轉換回來,我們有:

default_to_regular(d)
# {
#     "fruits": {
#         "apples": 17,
#         "oranges": 27,
#         "grapes": 44
#     },
#     "vegetables": {
#         "carrots": 21,
#         "beans": 12,
#         "peas": 9
#     },
#     "grains": 9,
#     "meats": 6
# }

您可以使用遞歸。 此解決方案在傳遞給merge的輸入中查找所有字典鍵,然后對每個鍵的值求和(如果值是整數)。 但是,如果值是字典,則再次調用merge

def merge(c):
  _keys = {i for b in c for i in b}
  return {i:[sum, merge][isinstance(c[0][i], dict)]([h[i] for h in c]) for i in _keys}

d = [{'fruits': {'apples': 1, 'oranges': 1, 'grapes': 2}, 'vegetables': {'carrots': 6, 'beans': 3, 'peas': 2}, 'grains': 4, 'meats': 1}, {'fruits': {'apples': 3, 'oranges': 5, 'grapes': 8}, 'vegetables': {'carrots': 7, 'beans': 4, 'peas': 3}, 'grains': 3, 'meats': 2}, {'fruits': {'apples': 13, 'oranges': 21, 'grapes': 34}, 'vegetables': {'carrots': 8, 'beans': 5, 'peas': 4}, 'grains': 2, 'meats': 3}]

import json
print(json.dumps(merge(d), indent=4))

輸出:

{
 "meats": 6,
 "grains": 9,
 "fruits": {
    "grapes": 44,
    "oranges": 27,
    "apples": 17
 },
"vegetables": {
     "beans": 12,
     "peas": 9,
     "carrots": 21
  }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM