Dizionario: Come elencare ogni sentiero chiave che contiene un certo valore?

voti
2

Consente di dire che ho un dizionario della forma:

d={'geo': {'bgcolor': 'white','lakecolor': 'white','caxis': {'gridcolor': 'white', 'linecolor': 'white',}},
    'title': {'x': 0.05},
    'yaxis': {'automargin': True,'linecolor': 'white','ticks': '','zerolinecolor': 'white','zerolinewidth': 2}
  }

Come si può lavorare il vostro senso attraverso che dict e fare una lista di ogni percorso chiave completo che contiene il valore 'white'? Utilizzo di una funzione definita da JFS dall'utente nel post Cerca un valore in un pitone dizionario nidificato consente di verificare se 'white'si verifica almeno una volta e anche restituisce il percorso:

# in:
def getpath(nested_dict, value, prepath=()):
    for k, v in nested_dict.items():
        path = prepath + (k,)
        if v == value: # found value
            return path
        elif hasattr(v, 'items'): # v is a dict
            p = getpath(v, value, path) # recursive call
            if p is not None:
                return p
getpath(d,'white')

# out:
('geo', 'bgcolor')

Ma 'bianco' si verifica anche in altri luoghi, come in:

1. d['geo']['lakecolor']

2: d['geo']['caxis']['gridcolor']

3: d['yaxis']['linecolor']

Come posso fare in modo che la funzione trova tutti i percorsi?

Ho provato applicando la funzione di cui sopra fino a tornare noneeliminando percorsi trovati uno per uno, ma che rapidamente trasformato in un brutto pasticcio.

Grazie per qualsiasi suggerimento!

È pubblicato 02/12/2019 alle 23:54
fonte dall'utente
In altre lingue...                            


3 risposte

voti
1

Ritornando è ciò che rende il risultato incompleto. Invece di restituire, utilizzare un elenco separato per monitorare i tuoi sentieri. Sto usando la lista cur_listqui, e il ritorno alla fine del ciclo:

d = {
  'geo': {'bgcolor': 'white',
         'caxis': {'gridcolor': 'white', 'linecolor': 'white'},
         'lakecolor': 'white'},
  'title': {'x': 0.05},
  'yaxis': {'automargin': True,
           'linecolor': 'white',
           'ticks': '',
           'zerolinecolor': 'white',
           'zerolinewidth': 2}
}

cur_list = []

def getpath(nested_dict, value, prepath=()):
    for k, v in nested_dict.items():
        path = prepath + (k,)
        if v == value: # found value
            cur_list.append(path)
        elif isinstance(v, dict): # v is a dict
            p = getpath(v, value, path, cur_list) # recursive call
            if p is not None:
                cur_list.append(p)

getpath(d,'white')
print(cur_list)


# RESULT:
# [('geo', 'bgcolor'), ('geo', 'caxis', 'gridcolor'), ('geo', 'caxis', 'linecolor'), ('geo', 'lakecolor'), ('yaxis', 'linecolor'), ('yaxis', 'zerolinecolor')]
Risposto il 03/12/2019 a 00:00
fonte dall'utente

voti
1

basta trasformare la funzione in modo che restituisce un liste non fare returnquando viene trovato qualcosa. Basta aggiungere a / estendere l'elenco

def getpath(nested_dict, value, prepath=()):
    p = []
    for k, v in nested_dict.items():
        path = prepath + (k,)
        if v == value: # found value
            p.append(path)
        elif hasattr(v, 'items'): # v is a dict
            p += getpath(v, value, path) # recursive call
    return p

con i dati di input, questo produce (ordine può variare a seconda delle versioni di Python dove dizionari sono non ordinate):

[('yaxis', 'linecolor'), ('yaxis', 'zerolinecolor'), ('geo', 'lakecolor'), 
('geo', 'caxis', 'linecolor'), ('geo', 'caxis', 'gridcolor'), ('geo', 'bgcolor')]
Risposto il 03/12/2019 a 00:00
fonte dall'utente

voti
5

Si tratta di un caso d'uso perfetto per scrivere un generatore:

def find_paths(haystack, needle):
    if haystack == needle:
        yield ()
    if not isinstance(haystack, dict):
        return
    for key, val in haystack.items():
        for subpath in find_paths(val, needle):
            yield (key, *subpath)

Si può usare come segue:

d = {
    'geo': {'bgcolor': 'white','lakecolor': 'white','caxis': {'gridcolor': 'white', 'linecolor': 'white',}},
    'title': {'x': 0.05},
    'yaxis': {'automargin': True,'linecolor': 'white','ticks': '','zerolinecolor': 'white','zerolinewidth': 2}
}

# you can iterate over the paths directly...
for path in find_paths(d, 'white'):
    print('found at path: ', path)

# ...or you can collect them into a list:
paths = list(find_paths(d, 'white'))
print('found at paths: ' + repr(paths))

L'approccio generatore ha il vantaggio che non è necessario creare un oggetto di mantenere tutti i percorsi in memoria contemporaneamente; possono essere trattati uno alla volta e immediatamente scartate. In questo caso, il risparmio di memoria sarebbe piuttosto modesta, ma in altri possono essere significativi. Inoltre, se un iterazione loop su un generatore viene terminata in anticipo, il generatore non ha intenzione di continuare a cercare altri percorsi che verrebbero successivamente scartati in ogni caso.

Risposto il 03/12/2019 a 00:18
fonte dall'utente

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more