Ваш ожидаемый результат не действителен. Я бы, наверное, go для вложенного словаря, подобного этому:
{
"Germany": {
"cases": {
"last_cases_value": 6025,
"updated_cases_value": 6026,
"change": 1
},
"active": {
"last_active_value": 100027,
"updated_active_value": 100026,
"change": -1
},
"deaths": {
"last_deaths_value": 1704,
"updated_deaths_value": 1706,
"change": 2
}
},
"Australia": {
"cases": {
"last_cases_value": 3045,
"updated_cases_value": 3046,
"change": 1
},
"active": {
"last_active_value": 100027,
"updated_active_value": 100028,
"change": 1
}
}
}
Чтобы получить вышеизложенное, я сначала преобразовал бы ваш список словарей во вложенные словари, где 'country'
- это ключ:
last_data = [{'country': 'USA', 'cases': 10425, 'deaths': 1704, 'recovered': 2525, 'active': 100027},
{'country': 'Australia', 'cases': 3045, 'deaths': 1704, 'recovered': 2525, 'active': 100027},
{'country': 'Germany', 'cases': 6025, 'deaths': 1704, 'recovered': 2525, 'active': 100027}]
current_data = [{'country': 'USA', 'cases': 10425, 'deaths': 1704, 'recovered': 2525, 'active': 100027},
{'country': 'Australia', 'cases': 3046, 'deaths': 1704, 'recovered': 2525, 'active': 100028},
{'country': 'Germany', 'cases': 6026, 'deaths': 1706, 'recovered': 2525, 'active': 100026}]
def list_dicts_to_nested_dict(key, lst):
return {dic[key]: {k: v for k, v in dic.items() if k != key} for dic in lst}
last_data_dict = list_dicts_to_nested_dict('country', last_data)
# {'USA': {'cases': 10425, 'deaths': 1704, 'recovered': 2525, 'active': 100027}, 'Australia': {'cases': 3045, 'deaths': 1704, 'recovered': 2525, 'active': 100027}, 'Germany': {'cases': 6025, 'deaths': 1704, 'recovered': 2525, 'active': 100027}}
current_data_dict = list_dicts_to_nested_dict('country', current_data)
# {'USA': {'cases': 10425, 'deaths': 1704, 'recovered': 2525, 'active': 100027}, 'Australia': {'cases': 3046, 'deaths': 1704, 'recovered': 2525, 'active': 100028}, 'Germany': {'cases': 6026, 'deaths': 1706, 'recovered': 2525, 'active': 100026}}
Что также является хорошей идеей, потому что поиск определенных данных c стран будет O (1) вместо O (N) при сканировании всего толковый словарь. Это также облегчает пересечение стран в будущем, что я покажу ниже.
Затем добавьте измененные данные во вложенную двухстороннюю collections.defaultdict
из dict
, поскольку она обрабатывает инициализацию новых ключей для вы. Вы можете взглянуть на этот Nested defaultdict of defaultdict answer для получения дополнительной информации и других способов сделать это.
result = defaultdict(lambda: defaultdict(dict))
# Get the intersecting keys.
# Avoids Key Errors in the future, if both dictionaries don't have the same key
for country in last_data_dict.keys() & current_data_dict.keys():
# Only deal with dictionaries that have changed
if last_data_dict[country] != current_data_dict[country]:
# Get intersecting keys between both dictionaries
for key in last_data_dict[country].keys() & current_data_dict[country].keys():
# Calculate the change between updated and previous data
change = current_data_dict[country][key] - last_data_dict[country][key]
# We only care about data that has changed
# Insert data into dictionary
if change != 0:
result[country][key][f"last_{key}_value"] = last_data_dict[country][key]
result[country][key][f"updated_{key}_value"] = current_data_dict[country][key]
result[country][key]["change"] = change
Затем вы можете сериализовать и вывести вышеуказанные данные в виде строки JSON с json.dumps
, поскольку таким способом проще вывести вложенный defaultdict
вместо преобразование всей структуры данных в dict
рекурсивно или каким-либо другим способом. В любом случае defaultdict
является подклассом dict
, поэтому его можно рассматривать как обычный словарь.
print(dumps(result, indent=4))
Кроме того, если вы не заботитесь о выводе, то печать defaultdict
напрямую также является простым вариантом:
print(result)
# defaultdict(<function <lambda> at 0x000002355BC3AA60>, {'Australia': defaultdict(<class 'dict'>, {'cases': {'last_cases_value': 3045, 'updated_cases_value': 3046, 'change': 1}, 'active': {'last_active_value': 100027, 'updated_active_value': 100028, 'change': 1}}), 'Germany': defaultdict(<class 'dict'>, {'deaths': {'last_deaths_value': 1704, 'updated_deaths_value': 1706, 'change': 2}, 'cases': {'last_cases_value': 6025, 'updated_cases_value': 6026, 'change': 1}, 'active': {'last_active_value': 100027, 'updated_active_value': 100026, 'change': -1}})})
В качестве дополнительной опции, но не нужно шаг, как было отмечено выше, мы можем создать рекурсивную функцию для преобразования вложенного defaultdict
в обычный словарь с подуровнями типа dict
:
def defaultdict_to_dict(df):
result = {}
for k, v in df.items():
if isinstance(v, defaultdict):
result[k] = dict(v)
defaultdict_to_dict(v)
return dict(result)
pprint(defaultdict_to_dict(result))
, который работает по назначению:
{'Australia': {'active': {'change': 1,
'last_active_value': 100027,
'updated_active_value': 100028},
'cases': {'change': 1,
'last_cases_value': 3045,
'updated_cases_value': 3046}},
'Germany': {'active': {'change': -1,
'last_active_value': 100027,
'updated_active_value': 100026},
'cases': {'change': 1,
'last_cases_value': 6025,
'updated_cases_value': 6026},
'deaths': {'change': 2,
'last_deaths_value': 1704,
'updated_deaths_value': 1706}}}
Вы можете посмотреть полную реализацию на ideone.com .