Woordenboek: Hoe wordt iedere toets pad dat een bepaalde waarde bevat een lijst?

stemmen
2

Laten we zeggen dat ik heb een woordenboek van de vorm gekregen:

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

Hoe kun je werk je weg door die dict en maak een lijst van elk volledige sleutel pad dat de waarde bevat 'white'? Met behulp van een functie gedefinieerd door de gebruiker JFS in de post zoeken naar een waarde in een geneste woordenboek python kunt u controleren of al dan niet 'white'optreedt ten minste één keer en ook de weg terug:

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

Maar 'witte' komt andere plaatsen, zoals in:

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

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

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

Hoe kan ik ervoor zorgen dat de functie vindt alle paden?

Ik heb geprobeerd het toepassen van de bovenstaande functie, totdat hij terugkeert none, terwijl het elimineren gevonden paden één voor één, maar dat werd snel een lelijke puinhoop.

Dank u voor uw suggesties!

De vraag is gesteld op 02/12/2019 om 23:54
bron van user
In andere talen...                            


3 antwoorden

stemmen
1

Terugkeren is wat het resultaat is onvolledig. In plaats van terug te keren, gebruik dan een aparte lijst aan uw paden te volgen. Ik gebruik lijst cur_listhier, en terug te sturen aan het einde van de lus:

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')]
antwoordde op 03/12/2019 om 00:00
bron van user

stemmen
1

gewoon transformeren uw functie, zodat het geeft een listen niet returnals er iets is gevonden. Voeg gewoon aan / uit te breiden in de lijst

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

met uw input data levert dit (order kan variëren afhankelijk van de python uitvoeringen geleverd, waarbij woordenboeken zijn ongeordende):

[('yaxis', 'linecolor'), ('yaxis', 'zerolinecolor'), ('geo', 'lakecolor'), 
('geo', 'caxis', 'linecolor'), ('geo', 'caxis', 'gridcolor'), ('geo', 'bgcolor')]
antwoordde op 03/12/2019 om 00:00
bron van user

stemmen
5

Dit is een perfecte use case om een ​​generator te schrijven:

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)

Je kunt het gebruiken als volgt:

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

De generator aanpak heeft het voordeel dat het niet nodig is om een ​​object om alle paden in het geheugen in een keer te houden te creëren; zij kan men worden verwerkt door één en onmiddellijk verwijderd. In dit geval zou het geheugen besparingen eerder bescheiden zijn, maar in andere gevallen aanzienlijk kunnen zijn. Ook als een lus itereren over een generator vroegtijdige beëindiging van de generator is niet van plan om te zoeken naar meer paden die later toch zou worden weggegooid te houden.

antwoordde op 03/12/2019 om 00:18
bron van user

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