2010-10-05 11 views
9

Voglio creare una funzione per attraversare in modo ricorsivo un dizionario multidimensionale, in cui le dimensioni sono sconosciute.attraversa in modo ricorsivo dizionario multidimensionale, dimensione sconosciuta

Ecco quello che ho trovato finora, ma non sembra funzionare correttamente. Questo stamperà alcune chiavi/valori due volte e non sono in ordine.

def walk_dict(d): 
    for k,v in d.items(): 
     if isinstance(v, dict): 
      walk_dict(v) 
     else: 
      print "%s %s" % (k, v) 

Ecco un allineamento del campione:

d = { 
     'plan_code': 'b', 
     'quantity': '1', 
     'account': { 
      'account_code': 'b', 
      'username': 'jdoe', 
      'email': '[email protected]', 
      'first_name': 'b', 
      'last_name': 'b', 
      'company_name': 'Company, LLC.', 
      'billing_info': { 
       'first_name': 'b', 
       'last_name': 'b', 
       'address1': '123 Test St', 
       'city': 'San Francisco', 
       'state': 'CA', 
       'country': 'US', 
       'zip': '94105', 
       'credit_card': { 
        'number': '1', 
        'year': '2018', 
        'month': '12', 
        'verification_value': '123', 
       }, 
      }, 
     }, 
    } 
+0

quindi qual è la tua domanda? – SingleNegationElimination

risposta

13

io non sono certo qual è il tuo obiettivo finale, ma il codice sta facendo quello che dovrebbe. Stai vedendo ciò che pensi siano ripetizioni di articoli perché ci sono combinazioni di chiavi/valori come "first_name": "b" che sono sia all'interno di "account" che all'interno di "billing_info" all'interno di "account". Non sono sicuro di quale ordine si sta cercando, ma dizionari sono non ordinata in modo che le funzioni di stamparli dovrà dare loro un po 'di ordine, per esempio sostituendo la seguente:

for k,v in d.items(): 

con

for k,v in sorted(d.items(),key=lambda x: x[0]): 

o avrai bisogno di un dizionario ordinato. È inoltre possibile utilizzare il modulo pprint in questo modo di dare una bella stampa di un dict:

>>> import pprint 
>>> pprint.pprint(d) 
{'account': {'account_code': 'b', 
      'billing_info': {'address1': '123 Test St', 
           'city': 'San Francisco', 
           'country': 'US', 
           'credit_card': {'month': '12', 
               'number': '1', 
               'verification_value': '123', 
               'year': '2018'}, 
           'first_name': 'b', 
           'last_name': 'b', 
           'state': 'CA', 
           'zip': '94105'}, 
      'company_name': 'Company, LLC.', 
      'email': '[email protected]', 
      'first_name': 'b', 
      'last_name': 'b', 
      'username': 'jdoe'}, 
'plan_code': 'b', 
'quantity': '1'} 

Tuttavia, io non sono completamente sicuro di quello che il vostro obiettivo finale è qui. Inoltre, mancano le chiavi quando i valori sono dizionari. Ho modificato il codice per fare una cosa simile a quello che pprint nella figura seguente:

def walk_dict(d,depth=0): 
    for k,v in sorted(d.items(),key=lambda x: x[0]): 
     if isinstance(v, dict): 
      print (" ")*depth + ("%s" % k) 
      walk_dict(v,depth+1) 
     else: 
      print (" ")*depth + "%s %s" % (k, v) 

che per il suo esempio rendimenti dict:

>>> walk_dict(d) 
account 
    account_code b 
    billing_info 
    address1 123 Test St 
    city San Francisco 
    country US 
    credit_card 
     month 12 
     number 1 
     verification_value 123 
     year 2018 
    first_name b 
    last_name b 
    state CA 
    zip 94105 
    company_name Company, LLC. 
    email [email protected] 
    first_name b 
    last_name b 
    username jdoe 
plan_code b 
quantity 1 
2

Ciò stampa la chiave, coppie di valori in modo corretto. Puoi indicare quali dati vengono ripetuti. Non ci può essere confusione sulla base dei dati di cui sopra come queste chiavi:

'first_name': 'b', 
'last_name': 'b', 

fanno parte di due dizionari - 'conto' e 'billing_info'. Quindi appariranno due volte in uscita.

Inoltre, se si desidera che un qualche tipo di ordine in cui i vostri dizionari K, V dovrebbe avere stampato uso ordinato Dizionari

+0

Ho appena afferrato quel codice e non ho notato la ripetizione. Pensavo che stavo facendo qualcosa di sbagliato. Grazie per la segnalazione. – imns

0

il codice funziona perfettamente bene, fa esattamente quello che hai detto a.

Le uniche ripetizioni che vedo nell'output sono first_name e last_name che vengono infatti definite due volte (in dizionari diversi).

Come "fuori servizio" È un dizionario, non ha un ordine garantito. beh forse lo fa, ma sarebbe basato sulla rappresentazione interna, su cui non dovresti fare affidamento.

Quello che devi fare è ordinare i dati nel modo in cui vuoi che escano. Potresti anche voler stampare il valore chiave del sottocomponente per rendere l'output più facile da capire.

1

come Justin Peelmentions, pprint.pprint probabilmente farà quello che vuoi.

Credo che il problema con il codice è che si dovrebbe stia stampando la chiave prima di recursing, cioè cambiare

if isinstance(v, dict): 
     walk_dict(v) 

a

if isinstance(v, dict): 
     print k 
     walk_dict(v) 

Anche se in ogni caso si sta andando a guardare piuttosto confusa a meno che tu non aggiunga indentazione e così via.

Questo genere di cose è in realtà piuttosto complicato; controlla il codice per pprint se vuoi avere qualche idea.

0

In Python, i dizionari sono indicizzati con le chiavi. Le chiavi possono essere di qualsiasi tipo immutabile come una stringa o un numero. A meno che le chiavi non siano ordinate, verranno sempre restituite in un ordine arbitrario. Per questo motivo, il tuo walk_dict sta stampando risultati apparentemente casuali.

Ecco un esempio di walk_dict che stampa tutte le chiavi e i valori. Ho ordinato le chiavi ad ogni livello del dizionario. Inoltre, ho stampato ciascun tasto. Il tuo codice non ha stampato una chiave prima che si ripetesse. Infine, ho aggiunto il riempimento delle stringhe per enfatizzare ogni livello del dizionario. I doctests stanno passando. Spero che questo ti aiuti a costruire la tua funzione finale.

import doctest 


def walk_dict(seq, level=0): 
    """Recursively traverse a multidimensional dictionary and print all 
    keys and values. 

    >>> d = {'dog': 'dusty', 'cat': 'fluffy', 'bird': 'chirpy'} 
    >>> walk_dict(d) 
    bird chirpy 
    cat fluffy 
    dog dusty 
    >>> d = {'location': 'home', 'animals':{'dog': 'dusty', 'cat': 'fluffy', 'bird': 'chirpy'}} 
    >>> walk_dict(d) 
    animals 
     bird chirpy 
     cat fluffy 
     dog dusty 
    location home 
    >>> d = {'location': 'home', 'animals':{'dog': 'dusty', 'cat': 'fluffy', 'bird': {'name':'chirpy', 'color':'blue'}}} 
    >>> walk_dict(d) 
    animals 
     bird 
     color blue 
     name chirpy 
     cat fluffy 
     dog dusty 
    location home 
    >>> d = { \ 
      'plan_code': 'b', \ 
      'quantity': '1', \ 
      'account': { \ 
       'account_code': 'b', \ 
       'username': 'jdoe', \ 
       'email': '[email protected]', \ 
       'first_name': 'b', \ 
       'last_name': 'b', \ 
       'company_name': 'Company, LLC.', \ 
       'billing_info': { \ 
        'first_name': 'b', \ 
        'last_name': 'b', \ 
        'address1': '123 Test St', \ 
        'city': 'San Francisco', \ 
        'state': 'CA', \ 
        'country': 'US', \ 
        'zip': '94105', \ 
        'credit_card': { \ 
         'number': '1', \ 
         'year': '2018', \ 
         'month': '12', \ 
         'verification_value': '123', \ 
        }, \ 
       }, \ 
      }, \ 
     } 
    >>> walk_dict(d) 
    account 
     account_code b 
     billing_info 
     address1 123 Test St 
     city San Francisco 
     country US 
     credit_card 
      month 12 
      number 1 
      verification_value 123 
      year 2018 
     first_name b 
     last_name b 
     state CA 
     zip 94105 
     company_name Company, LLC. 
     email [email protected] 
     first_name b 
     last_name b 
     username jdoe 
    plan_code b 
    quantity 1 
    """ 
    items = seq.items() 
    items.sort() 
    for v in items: 
     if isinstance(v[1], dict): 
      # Print the key before make a recursive call 
      print "%s%s" % (" " * level, v[0]) 
      nextlevel = level + 1 
      walk_dict(v[1], nextlevel) 
     else: 
      print "%s%s %s" % (" " * level, v[0], v[1]) 


if __name__ == '__main__': 
    doctest.testmod() 
3

>>> import json

>>> print json.dumps(d, indent=4)

+1

Nel mio caso ho bisogno di farlo proprio perché 'json.dumps' non può gestire alcuni degli oggetti nella struttura. – OrangeDog

+0

La libreria 'jsonpickle' fa un lavoro sorprendentemente buono di rappresentare i tipi di oggetti' json' strozzati - provatelo. –

1

Ecco una variante della risposta accettata da Justin Peel che restituisce il risultato come un OrderedDict anziché stampare il risultato.

from collections import OrderedDict 

def sort_by_keys(dct,): 
    new_dct = OrderedDict({}) 
    for key, val in sorted(dct.items(), key=lambda (key, val): key): 
     if isinstance(val, dict): 
      new_dct[key] = sort_by_keys(val) 
     else: 
      new_dct[key] = val 
    return new_dct 
Problemi correlati