Verschachtelte Dictionarys - Value ersetzen

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
Vorlif
User
Beiträge: 3
Registriert: Samstag 27. August 2011, 19:05

Hallo Leute,

ich hoffe Ihr könnt mir helfen. :)
Ich will auch gar nicht groß herum reden sondern gleich zum Thema kommen.

Ich habe als Eingabeformat ein paar JSON Dokumente mit verschachtelten Dictionarys.
Aus diesem Dictionary habe ich bestimmte Keys heraus gelesen und auch abgespeichert.
Die Ebenen der Dictionarys sind immer unterschiedlich aber ich weiß in welche Ebene ich muss und kennen auch die jeweiligen Keys.

Bsp:

Code: Alles auswählen

# Dictionary
dn = {
   "1": "Test_1",
   "2": "Test_2",
   "3": {
      "3.1": "Test_3.1",
      "3.2": "Test_3.2",
      "3.3": {
         "3.3.1": "Test_3.3.1"
         }
}
# Ausgabe
dn["3"]["3.3"]["3.3.1]
>>> Test_3.3.1
Ich weiß also, wie schon geschrieben, meine Keys und möchte jetzt die jeweilige Value ersetzen:

Code: Alles auswählen

dn["3"]["3.3"]["3.3.1] = "Test"
Nun sind die Keys nicht immer in der dritten Ebene sondern manchmal auch in der 2., 4. etc. und ich möchte die Values ganz unabhängig ersetzen.
Ich weiß leider nur nicht wie. Das einzige was mir eingefallen wäre ist eval aber die Lösung gefällt mir nicht.

Code: Alles auswählen

# Mir bekannte Keys zu meiner gesuchten Ebene
dict_keys = ["3", "3.3", "3.3.1"]

# Dict-Abfrage als Stinge "bauen"
select = str(dictonary)
for dict_key in dict_keys:
   select += "[dict_key]"

# Dict-Value ersetzen
eval(select) = "Test"
Ich hoffe ich habe es ausreichend erklärt und Ihr versteht mein Problem .^^

Habt ihr vllt. eine Lösung?
Habe ich vllt. irgendetwas übersehen? :K

Danke schonmal für eure Hilfe. :)
Benutzeravatar
pillmuncher
User
Beiträge: 1529
Registriert: Samstag 21. März 2009, 22:59
Wohnort: Pfaffenwinkel

Dein Beispielprogramm enthält Syntaxfehler. Auch ohne die würde es nicht funktionieren. Schau es dir nochmal an.

Um dein Problem zu lösen, würde ich ungefähr sowas machen:

Code: Alles auswählen

>>> def get_from_path(mapping, key, *keys):
...     if keys:
...         return get_from_path(mapping[key], *keys)
...     return mapping[key]
...
>>>
>>> print get_from_path(dn, '3', '3.3', '3.3.1')
Test_3.3.1
Rekursiv also. Oder iterativ:

Code: Alles auswählen

>>> def get_from_path(mapping, key, *keys):
...     result = mapping[key]
...     for key in keys:
...         result = result[key]
...     return result
...
>>>
>>> print get_from_path(dn, '3', '3.3', '3.3.1')
Test_3.3.1
In specifications, Murphy's Law supersedes Ohm's.
BlackJack

@Vorlif: Ich würde sagen Du hast übersehen, dass Du nicht alles auf einmal auswerten musst, sondern in der Schleife einfach in jedem Durchlauf das nächste Wörterbuch abfragen kannst.

Code: Alles auswählen

from pprint import pprint


def main():
    data = {
        '1': 'Test_1',
        '2': 'Test_2',
        '3': {
            '3.1': 'Test_3.1',
            '3.2': 'Test_3.2',
            '3.3': {
                '3.3.1': 'Test_3.3.1'
            }
        }
    }
    
    keys = ['3', '3.3', '3.3.1']
    sub_dict = data
    for key in keys[:-1]:
        sub_dict = sub_dict[key]
    sub_dict[keys[-1]] = 'Test'

    pprint(data)


if __name__ == '__main__':
    main()
Benutzeravatar
pillmuncher
User
Beiträge: 1529
Registriert: Samstag 21. März 2009, 22:59
Wohnort: Pfaffenwinkel

Oder generisch-katamorphisch:

Code: Alles auswählen

>>> def get_from_path(mapping, key, *keys):
...     return reduce(type(mapping).get, keys, mapping[key])
...
>>> print get_from_path(dn, '3', '3.3', '3.3.1')
Test_3.3.1
Wenn get_from_path() auch ohne keys aufgerufen werden darf (um das ganze mapping zurückzugeben), geht auch das hier:

Code: Alles auswählen

>>> def get_from_path(mapping, *keys):
...     return reduce(type(mapping).get, keys, mapping)
...
>>> print get_from_path(dn, '3', '3.3', '3.3.1')
Test_3.3.1
>>> print get_from_path(dn)
{'1': 'Test_1', '3': {'3.2': 'Test_3.2', '3.3': {'3.3.1': 'Test_3.3.1'}, '3.1': 'Test_3.1'}, '2': 'Test_2'}
Beide Varianten haben allerdings den Nachteil, dass alle geschachtelten Mappings vom selben Typ sein müssen. Wenn das nicht immer der Fall ist, kann man es so lösen:

Code: Alles auswählen

def get_from_path(mapping, *keys):
    return reduce(lambda m, k: m[k], keys, mapping)
Das Ersetzen geht damit übrigens so:

Code: Alles auswählen

>>> get_from_path(dn, '3', '3.3')['3.3.1'] = 'huhu'
>>> print get_from_path(dn, '3', '3.3', '3.3.1')
huhu
Oder man schreibt sich eine Funktion:

Code: Alles auswählen

>>> def set_in_path(mapping, item, *keys):
...     reduce(type(mapping).get, keys[:-1], mapping)[keys[-1]] = item
...
>>> set_in_path(dn, 'test', '3', '3.3', '3.3.1')
>>> print get_from_path(dn, '3', '3.3', '3.3.1')
test
Zuletzt geändert von pillmuncher am Dienstag 29. Juli 2014, 22:35, insgesamt 1-mal geändert.
In specifications, Murphy's Law supersedes Ohm's.
BlackJack

Anstelle des ``lambda`` könnte man auch `operator.getitem()` verwenden. :-)
Benutzeravatar
pillmuncher
User
Beiträge: 1529
Registriert: Samstag 21. März 2009, 22:59
Wohnort: Pfaffenwinkel

BlackJack hat geschrieben:`operator.getitem()`
Oh Mann, das hatte ich ganz vergessen. :roll:
In specifications, Murphy's Law supersedes Ohm's.
Vorlif
User
Beiträge: 3
Registriert: Samstag 27. August 2011, 19:05

Wow Leute,

ganz, ganz vielen Dank, für die vielen Lösungen. :)
Hier gefällt es mir.

Die Fehler in meinen Beispiel habe ich gefunden. Ihr habt zum Glück gewusst was ich meine.
Antworten