Dictionnaire: Comment lister tous les chemins clé qui contient une certaine valeur?

voix
2

Disons que j'ai un dictionnaire de la forme:

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

Comment pouvez - vous travailler votre chemin à travers cette dict et faire une liste de chaque chemin clé complet qui contient la valeur 'white'? En utilisant une fonction définie par l' utilisateur JFS dans le poste de recherche d'une valeur dans un python dictionnaire imbriqué vous permet de vérifier si oui ou non 'white'se produit au moins une fois et retourne aussi le chemin:

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

Mais « blanc » se produit aussi d'autres endroits, comme dans:

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

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

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

Comment puis-je faire en sorte que la fonction trouve tous les chemins?

J'ai essayé d' appliquer la fonction ci - dessus jusqu'à ce qu'il retourne nonetout en éliminant les chemins trouvés un par un, mais qui se transforma rapidement en un désordre laid.

Merci pour toutes les suggestions!

Créé 02/12/2019 à 23:54
source utilisateur
Dans d'autres langues...                            


3 réponses

voix
1

De retour est ce qui rend le résultat incomplet. Au lieu de retourner, utiliser une liste séparée pour suivre vos chemins. J'utilise la liste cur_listici, et le retour à la fin de la boucle:

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')]
Créé 03/12/2019 à 00:00
source utilisateur

voix
1

juste transformer votre fonction il retourne listet ne pas returnquand quelque chose se trouve. Il suffit d' ajouter à / étendre la liste

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

avec vos données d'entrée, ce produit (l'ordre peut varier selon les versions de Python où les dictionnaires ne sont pas ordonnés):

[('yaxis', 'linecolor'), ('yaxis', 'zerolinecolor'), ('geo', 'lakecolor'), 
('geo', 'caxis', 'linecolor'), ('geo', 'caxis', 'gridcolor'), ('geo', 'bgcolor')]
Créé 03/12/2019 à 00:00
source utilisateur

voix
5

Ceci est un cas parfait d'utiliser pour écrire un générateur:

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)

Vous pouvez l'utiliser comme suit:

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'approche du générateur a l'avantage qu'il n'a pas besoin de créer un objet pour garder tous les chemins de la mémoire à la fois; ils peuvent être traités un par un et immédiatement mis au rebut. Dans ce cas, les économies de mémoire seraient plutôt modestes, mais dans d'autres, ils peuvent être importants. En outre, si une itérer de boucle sur un générateur se termine plus tôt, le générateur ne va pas continuer à chercher plusieurs chemins qui seront plus tard mis au rebut de toute façon.

Créé 03/12/2019 à 00:18
source utilisateur

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