Slovník: Jak vypsat každou cestu ke klíči, který obsahuje určitou hodnotu?

hlasů
2

Řekněme mám slovník tvaru:

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}
  }

Jak můžete pracovat si cestu přes tuto dict a vytvořit seznam každého úplného klíčového cestu, která obsahuje hodnotu 'white'? Použití funkce definované uživatelem JFS v post Vyhledat hodnoty v vnořené slovníku python můžete zkontrolovat, zda se 'white'vyskytuje alespoň jednou a také vrátí cestu:

# 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')

Ale ‚bílý‘ se vyskytuje na jiných místech také, jako v:

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

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

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

Jak se mohu ujistit, že funkce najde všechny cesty?

Snažil jsem se použití funkce výše, až se vrátí nonea zároveň eliminuje nalezené cesty jeden po druhém, ale to se rychle změnil na ošklivé nepořádek.

Děkuji za jakékoliv návrhy!

Položena 02/12/2019 v 23:54
zdroj uživatelem
V jiných jazycích...                            


3 odpovědí

hlasů
1

Po návratu je to, co dělá výsledek neúplné. Namísto vracení použít samostatný seznam sledovat své cesty. Já používám seznam cur_listtady, a vracet to na samém konci smyčky:

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')]
Odpovězeno 03/12/2019 v 00:00
zdroj uživatelem

hlasů
1

Jen transformovat své funkci, tak to vrátí list, a ne return, když je nalezen něco. Stačí přidat do / rozšířit seznam

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

s vstupními daty, to produkuje (pořadí se může lišit v závislosti na python verze kde slovníky jsou neuspořádané):

[('yaxis', 'linecolor'), ('yaxis', 'zerolinecolor'), ('geo', 'lakecolor'), 
('geo', 'caxis', 'linecolor'), ('geo', 'caxis', 'gridcolor'), ('geo', 'bgcolor')]
Odpovězeno 03/12/2019 v 00:00
zdroj uživatelem

hlasů
5

To je ideální případ užití psát generátor:

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)

Můžete jej použít následujícím způsobem:

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))

Přístup Generátor má tu výhodu, že není nutné vytvořit objekt, aby všechny cesty v paměti najednou; mohou být zpracovány jeden po druhém a okamžitě zlikvidovat. V tomto případě je úspora paměti by bylo velmi skromné, ale v jiných to může být významné. Také v případě, smyčka iteraci přes generátoru je ukončeno předčasně, generátor nebude pokračovat v hledání pro více cest, které by byly později vyřazeny stejně.

Odpovězeno 03/12/2019 v 00:18
zdroj uživatelem

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