Словарь: Как перечислить все ключевые пути, который содержит определенное значение?

голоса
2

Допустим, у меня есть словарь формы:

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

Как вы можете работать ваш путь через этот Dict и составить список каждого полного пути ключа , который содержит значение 'white'? Используя функцию , определяемую пользователем JFS в пост Поиск значения в гнездовой словарь питона позволяет проверить , является ли или не 'white'происходит , по крайней мере один раз , а также возвращает путь:

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

Но «белый» происходит в других местах тоже, как в:

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

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

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

Как я могу убедиться, что функция находит все пути?

Я пытался применять функцию выше , пока она не вернется none, устраняя найденные пути один за другим, но быстро превратился в уродливую беспорядок.

Спасибо за любые предложения!

Задан 02/12/2019 в 23:54
источник пользователем
На других языках...                            


3 ответов

голоса
1

Возвращаясь, что делает результат неполной. Вместо того , чтобы вернуться, использовать отдельный список для отслеживания пути. Я использую список cur_listздесь, и возвращение его в самом конце цикла:

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')]
Ответил 03/12/2019 в 00:00
источник пользователем

голоса
1

просто превратить вашу функцию , так что возвращает listи не returnкогда - то найдено. Просто добавьте / расширить список

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

с входными данными, это производит (заказ может варьироваться в зависимости от версии питона, где словари неупорядоченные):

[('yaxis', 'linecolor'), ('yaxis', 'zerolinecolor'), ('geo', 'lakecolor'), 
('geo', 'caxis', 'linecolor'), ('geo', 'caxis', 'gridcolor'), ('geo', 'bgcolor')]
Ответил 03/12/2019 в 00:00
источник пользователем

голоса
5

Это идеальный случай использования, чтобы написать генератор:

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)

Вы можете использовать его следующим образом:

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

Подход генератора имеет то преимущество, что не нужно создавать объект, чтобы сохранить все пути в памяти одновременно; они могут быть обработаны один за другим и сразу же отбрасывается. В этом случае экономия памяти будет довольно скромной, но в других случаях они могут быть значительными. Кроме того, если итерация перебирает генератор прекращается рано, генератор не будет продолжать поиск более путей, которые будут позже выброшенными в любом случае.

Ответил 03/12/2019 в 00:18
источник пользователем

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