2010-08-04 3 views
9

Мне пришлось удалить некоторые поля из словаря, ключи от этих полей находятся в списке. Итак, я пишу эту функцию:Элегантный способ удаления полей из вложенных словарей

def delete_keys_from_dict(dict_del, lst_keys): 
    """ 
    Delete the keys present in the lst_keys from the dictionary. 
    Loops recursively over nested dictionaries. 
    """ 
    dict_foo = dict_del.copy()#Used as iterator to avoid the 'DictionaryHasChanged' error 
    for field in dict_foo.keys(): 
     if field in lst_keys: 
      del dict_del[field] 
     if type(dict_foo[field]) == dict: 
      delete_keys_from_dict(dict_del[field], lst_keys) 
    return dict_del 

Этот код работает, но это не очень элегантно, и я уверен, что вы можете закодировать лучшее решение.

+0

хммм я нахожу это элегантным! –

+3

Я думаю, что это неплохой код; у вас есть важный бит, который рекурсирует по вложенным словарям. Вероятно, вы должны проверить 'isinstance (spam, collections.MutableMapping)' более политически. – katrielalex

ответ

15
def delete_keys_from_dict(dict_del, lst_keys): 
    for k in lst_keys: 
     try: 
      del dict_del[k] 
     except KeyError: 
      pass 
    for v in dict_del.values(): 
     if isinstance(v, dict): 
      delete_keys_from_dict(v, lst_keys) 

    return dict_del 
+1

Извините, но этот код не работает должным образом. Я стараюсь: print delete_keys_from_dict ({'code': 'sdasda', 'tag.dbmko8e8': {'id': 'casas', 'name': ' asdas identyfier '},' name ':' collection '}, ["id"]) И удалить все поля из словаря :( – fasouto

+1

Я не возвращал словарь (я обновил код выше). Вы получали «None», потому что значение не возвращалось. Поскольку эта функция не изменяет словарь, вы можете просто распечатать тот же словарь, в котором вы проходили. Я обновил код, чтобы он также возвращал dict. –

+1

tbh Я думаю, что ваша версия fisrt была лучше, не возвращая словарь, потому что, как вы сказали, у оригинала уже есть обновленные ключи, и вы не «теряете» возвращаемое значение, чтобы вернуть что-то уже существующее, и метод может быть в будущем, чтобы возвратить, например, количество значений, удаленных без изменений в уже существующий код вызова. – laurent

3

Поскольку вы уже должны перебрать каждый элемент в Словаре, я бы придерживаться одного цикла и просто убедитесь, что использовать набор для поиска ключей для удаления

def delete_keys_from_dict(dict_del, the_keys): 
    """ 
    Delete the keys present in the lst_keys from the dictionary. 
    Loops recursively over nested dictionaries. 
    """ 
    # make sure the_keys is a set to get O(1) lookups 
    if type(the_keys) is not set: 
     the_keys = set(the_keys) 
    for k,v in dict_del.items(): 
     if k in the_keys: 
      del dict_del[k] 
     if isinstance(v, dict): 
      delete_keys_from_dict(v, the_keys) 
    return dict_del 
+0

@Ned Batchelder: есть ли способ, которым мы можем отменить это? Я имею в виду только сохранить определенные ключи и удалить остальные, которые не входят в список? –

6

Поскольку вопрос запросил элегантный способ, я представлю свое универсальное решение для борьбы с вложенными структурами. Во-первых, установить boltons utility package с pip install boltons, то:

from boltons.iterutils import remap 

data = {'one': 'remains', 'this': 'goes', 'of': 'course'} 
bad_keys = set(['this', 'is', 'a', 'list', 'of', 'keys']) 

drop_keys = lambda path, key, value: key not in bad_keys 
clean = remap(data, visit=drop_keys) 
print(clean) 

# Output: 
{'one': 'remains'} 

Короче говоря, the remap utility является полнофункциональной, но лаконичный подход к решению реальных структур данных, которые часто вложенными, и даже может содержать циклы и специальные контейнеры.

This page имеет еще много примеров, в том числе те, которые работают с гораздо большими объектами из API Github.

Это чистый-Python, поэтому он работает повсюду и полностью протестирован в Python 2.7 и 3.3+. Лучше всего, я написал это для подобных случаев, так что, если вы найдете случай, который он не обрабатывает, вы можете исправить ошибку, чтобы исправить ее right here.

+0

Аккуратно! :) Спасибо. – darkless

0

Используя удивительный код из this поста и добавить небольшое заявление:

def remove_fields(self, d, list_of_keys_to_remove): 
     if not isinstance(d, (dict, list)): 
      return d 
     if isinstance(d, list): 
      return [v for v in (self.remove_fields(v, list_of_keys_to_remove) for v in d) if v] 
     return {k: v for k, v in ((k, self.remove_fields(v, list_of_keys_to_remove)) for k, v in d.items()) if k not in list_of_keys_to_remove} 
0

я думаю следующее изящнее:

def delete_keys_from_dict(dict_del, lst_keys): 
    if not isinstance(dict_del, dict): 
     return dict_del 
    return {key:value for key,value in ((key, delete_keys_from_dict(value)) for key,value in dict_del.items()) if key not in lst_keys} 
Смежные вопросы