función python3 para fusionar diccionarios
Tengo un montón de registros JSON, cada registro con un grado variable de integridad. En otras palabras, el registro A puede contener claves que no están en el registro B y viceversa. Para obtener una mejor comprensión de los datos que contiene, he creado una función para tomar una cantidad N de registros y fusionarlos, creando un solo registro de Frankenstein que contiene todas las claves y un solo valor para cada clave.
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 es la salida del 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"
]
}
He probado la función y funciona. Lo que me gustaría es algún comentario con respecto al código. ¿Estoy haciendo esto de la manera más eficiente posible? ¿Hay mejores formas de hacerlo?
Respuestas
El keyparámetro to frankenstein()no parece usarse en ninguna parte y puede eliminarse.
Use líneas en blanco para dividir el código en partes lógicas más pequeñas. Ayuda a leer y comprender el código.
in_dictes un nombre engañoso, porque puede ser una lista, un dictado o cualquier otra cosa.
En el for k, v in in_dict.items():ciclo, in_dict[k]y vson lo mismo, vse prueban 3 veces, y el valor de retorno de setdefault()se descarta y luego se busca en la siguiente línea. Se puede reescribir así:
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
La lógica para el manejo listsparece enrevesada. Por ejemplo, parece que spuede actualizarse cada vez que pasa por el ciclo y también agregarse a out[k]. La pregunta no establece claramente las reglas para combinar cosas, por lo que tal vez sea correcta. Sería útil un comentario o una cadena de documentos que explique el propósito de la función y las reglas para fusionar valores.