função python3 para mesclar dicionários
Eu tenho um monte de registros JSON, cada registro com um grau variável de completude. Em outras palavras, o registro A pode conter chaves que não estão no registro B e vice-versa. Para entender melhor os dados contidos, criei uma função para pegar um número N de registros e mesclá-los, criando um único registro frankenstein contendo todas as chaves e um único valor para cada chave.
import sys
import json
def frankenstein(out, in_dict, key=None):
if isinstance(in_dict, dict):
for k, v in in_dict.items():
if isinstance(in_dict[k], list) and v:
out.setdefault(k, [])
frankenstein(out[k], v, k)
elif isinstance(in_dict[k], dict) and v:
out.setdefault(k, {})
frankenstein(out[k], v, k)
elif v:
out[k] = v
elif isinstance(in_dict, list):
s = {}
for item in in_dict:
if isinstance(item, dict):
frankenstein(s, item)
elif not out:
out.append(item)
if s:
if not out:
out.append(s)
else:
frankenstein(s, out[0])
out[0] = s
if __name__ == '__main__':
l = [
{
"name": "foo bar",
"experience": [
{
"company": {
"name": "oracle",
"hq": "123 main st",
"size": 100
},
"function": [
{
"name": "go getter"
}
],
"location": {
"doubleday": "publisher"
},
"animal": "horse"
}
],
"skills": ["programming", "eating"]
},
{
"name": "poo dar",
"experience": [
{
"company": {
"name": "microsoft",
"url": "foo.bar/com"
},
"function": [
{
"name": "bread",
"level": "really high"
}
],
"solitary": {
"fat": "cat"
},
"health": "no good"
}
],
"skills": ["igz"]
},
{
"name": "poo mar",
"experience": [
{
"function": [
{
"zoo": "creature"
}
],
"location": {
"taste": "food"
},
"ping": {
"pong": "bong"
}
}
],
"skills": ["woots own"]
}
]
out = {}
for item in l:
frankenstein(out, item)
print(json.dumps(out, indent=4))
Esta é a saída do código:
{
"name": "poo mar",
"experience": [
{
"function": [
{
"name": "bread",
"level": "really high",
"zoo": "creature"
}
],
"location": {
"taste": "food",
"doubleday": "publisher"
},
"ping": {
"pong": "bong"
},
"company": {
"name": "oracle",
"url": "foo.bar/com",
"hq": "123 main st",
"size": 100
},
"solitary": {
"fat": "cat"
},
"health": "no good",
"animal": "horse"
}
],
"skills": [
"programming"
]
}
Já testei a função e funciona. O que eu gostaria é de algum feedback em relação ao código. Estou fazendo isso da maneira mais eficiente possível? Existem maneiras melhores de fazer isso?
Respostas
O keyparâmetro a frankenstein()não parece ser usado em nenhum lugar e pode ser eliminado.
Use linhas em branco para dividir o código em pedaços lógicos menores. Auxilia na leitura e compreensão do código.
in_dicté um nome enganoso, porque pode ser uma lista, um dict ou outra coisa qualquer.
No for k, v in in_dict.items():loop, in_dict[k]e vsão a mesma coisa, vé testado 3 vezes, e o valor de retorno de setdefault()é descartado e então consultado na próxima linha. Pode ser reescrito assim:
for k, v in in_dict.items():
if not v:
continue
if isinstance(v, (list, dict)):
out_k = out.setdefault(k, v.__class__())
frankenstein(out_k, v)
else:
out[k] = v
A lógica para o manuseio listsparece complicada. Por exemplo, parece que spode ser atualizado a cada vez através do loop e também anexado ao out[k]. A pergunta não indica claramente as regras para combinar as coisas, então talvez esteja correta. Um comentário ou doc-string explicando a finalidade da função e as regras para mesclar valores seria útil.