Одна местная компания производит обновление данных о пользователях и заодно решили реорганизовать систему хранения.
Напишите программу, которая обновляет данные о пользователях, записанных в JSON файле.
Формат ввода
Пользователь вводит два имени файла.
В первом хранится JSON массив пользователей.
Во втором — массив новых данных.
Информация о каждом пользователе представляется JSON объектом, в котором обязательно присутствует поле name
, описывающее имя пользователя. Остальные поля являются дополнительными.
Формат вывода
В первый файл запишите информацию о пользователях в виде JSON объекта, ключами которого выступают имена пользователей, а значениями — объекты с информацией о них.
Если какая-либо дополнительная информация о пользователе изменяется, то требуется сохранить лексикографически большее значение.
Пример
Ввод
# Пользовательский ввод:
users.json
updates.json
# Содержимое файла users.json
[
{
"name": "Ann",
"address": "Flower st."
},
{
"name": "Bob",
"address": "Summer st.",
"phone": "+7 (123) 456-78-90"
}
]
# Содержимое файла updates.json
[
{
"name": "Ann",
"address": "Awesome st.",
"phone": "+7 (098) 765-43-21"
},
{
"name": "Bob",
"address": "Winter st."
}
]
Вывод
# Содержимое файла users.json
{
"Ann": {
"address": "Flower st.",
"phone": "+7 (098) 765-43-21"
},
"Bob": {
"address": "Winter st.",
"phone": "+7 (123) 456-78-90"
}
}
Решение
Эта задача вызывает больше всего вопросов учеников. В основном они связаны с тем, что не хватает стройности мышления при подходе к решению задачи. Попытка работать со списком словарей в каждом из которых одно из полей – главное сбивает с толку и запутывает. Но если при считывании файла и разборе каждого словаря из списка сразу переделывать его в соотвествующий словарь, то все становится на свои места.
В решениях представлено два варианта – “академическое”, когда исходный файл сразу считывается в правильный словарь, и операции обновления уже идут с правильным словарем. И более короткий с точки зрения кода, но менее эффективный с точки зрения производительности код, который формирует правильный словарь только после того, как определились какое из значений нужно записать в целевой словарь.
Посмотреть код
Решение
import json
json_name = input()
json_update = input()
with open(json_name) as file:
source = json.load(file)
with open(json_update) as file:
updates = json.load(file)
name_key = 'name'
new_dict = {}
for data in source:
name = data.pop(name_key)
new_dict[name] = data
for data in updates:
name = data.pop(name_key)
if name not in new_dict: # на случай,если исходный словарь не содержал такого имени
new_dict[name] = {}
for key in data.keys():
if new_dict[name].get(key, '') < data[key]:
new_dict[name][key] = data[key]
with open(json_name, 'w') as file:
json.dump(new_dict, file, sort_keys=False, indent=4, ensure_ascii=False)
Решение
import json
json_name = input()
json_update = input()
with open(json_name) as file:
source = json.load(file)
with open(json_update) as file:
updates = json.load(file)
name_key = 'name'
new_dict = {}
for update in updates:
for data in source:
if update[name_key] == data[name_key]:
for key in update.keys():
if update[key] > data.get(key, ''):
data[key] = update[key]
for data in source:
name = data.pop(name_key)
new_dict[name] = data
with open(json_name, 'w') as file:
json.dump(new_dict, file, sort_keys=False, indent=4, ensure_ascii=False)
Задача N(n)
“””Слияние данных”””
import json
file_out = ‘users.json’
update_file = ‘updates.json’
# file_out = input().strip()
# update_file = input().strip()
with open(file_out, ‘r+’, encoding=”UTF-8″) as file:
users = {}
old_info = json.load(file)
for index in range(len(old_info)):
name = old_info[index][‘name’]
value = {}
users[name] = value
for key in old_info[index].keys():
if key != ‘name’:
value[key] = old_info[index][key]
with open(update_file, ‘r’, encoding=”UTF-8″) as update:
update_info = json.load(update)
for dict in update_info:
name = dict[‘name’]
for k in dict.keys():
if k != ‘name’:
if users[name].get(k, ”) < dict[k]:
users[name][k] = dict[k]
file.seek(0)
json.dump(users, file, ensure_ascii=False, indent=4)
не могу придумать тест, чтобы проверить правильность решения
постарался написать код ещё раз по вашему алгоритму
можете, пожалуйста, дать подсказку?
Пришлось отформатировать код, чтобы протестировать.
Я мог бы предположить, что во втором тесте в файле updates есть имя, которого нет в файле users.
В моих алгоритмах в одном случае это приводит к созданию нового пользователя, в другом просто игнорирует его, а в вашем приводит к ошибке, поскольку такого ключа в момент сравнения
if users[name].get(k, ”) < dict[k]:
в словаре нет.
Но у вас ошибка WA – неверный ответ, в то время как описанная мной ситуация привела бы к ошибке RE – ошибка во время исполнения. Да и один из моих алгоритмов был бы просто неверен в итоге, ведь ведут они себя по-разному.
Полагаю тут есть над чем подумать более внимательно.
ps. использование dict в качестве имени переменной не очень хорошее правило. это зарезервированное слово.
Услышал, буду перепроверять перед отправкой, извиняюсь
Я немного переделал логику вашего решения и оно сработало. Сразу скажу, что этап составления словаря и этап обновления словаря у вас нормальный, я его поправил только с целью лучшей читабельности – заменил цикл с индексов на объекты в обновлении, да поправил название переменных.
Под подозрением остается работа с файлами.
Вот рабочий вариант вашего кода:
Ошибку может вызвать, например, способ открытия файла r+ с дальнейшим seek(0). Дело в том, что если результат окажется короче оригинала, то в конце файла останется мусор, который и приведет к тому, что файл-результат не совпадет с ожидаемым результатом. А все потому, что seek() не обрезает файл на последнем измененном символе – у него другое предназначение.
к примеру если изначально файл был
12345
потом мы после чтения сделали seek(0) и записали туда
XXX
то в результате мы получим
XXX45