Funkcja python3 do łączenia słowników
Mam kilka rekordów JSON, z których każdy ma inny stopień kompletności. Innymi słowy, rekord A może zawierać klucze, których nie ma w rekordzie B i na odwrót. Aby lepiej zrozumieć zawarte w nich dane, stworzyłem funkcję, która pobiera N rekordów i łączy je razem, tworząc jeden rekord frankensteina zawierający wszystkie klucze i jedną wartość dla każdego klucza.
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))
Oto wynik z kodu:
{
"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"
]
}
Przetestowałem tę funkcję i działa. Chciałabym uzyskać opinię dotyczącą kodu. Czy robię to w najbardziej efektywny sposób? Czy są na to lepsze sposoby?
Odpowiedzi
Wydaje się, że keyparametr frankenstein()to nie jest nigdzie używany i można go wyeliminować.
Użyj pustych linii, aby podzielić kod na mniejsze logiczne fragmenty. Pomaga w czytaniu i zrozumieniu kodu.
in_dict to myląca nazwa, ponieważ może to być lista, dyktando lub coś zupełnie innego.
W for k, v in in_dict.items():pętli, in_dict[k]i vto samo, vtestuje się 3 razy, a wartość powrotu setdefault()jest odrzucana, a następnie sprawdzić na następnej linii. Można to przepisać tak:
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
Logika obsługi listswydaje się zagmatwana. Na przykład wygląda na to, że smoże być aktualizowany za każdym razem przez pętlę, a także dołączany do out[k]. Pytanie nie określa jasno zasad łączenia rzeczy, więc może jest poprawne. Pomocny byłby komentarz lub ciąg dokumentu wyjaśniający cel funkcji i zasady łączenia wartości.