views:

41

answers:

2

Hi! I had to remove some fields from a dictionary, the keys of this fields are on a list. So I write this function:

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

This code works, but it's not very elegant and I'm sure that you can code a better solution. Thaks in advance.

+3  A: 
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
Ned Batchelder
Sorry but this code doesn't work as expected I try to do: print delete_keys_from_dict ({'code': 'sdasda', 'tag.dbmko8e8': {'id':'casas', 'name': 'asdas identyfier'}, 'name': 'collection'}, ["id"])And delete all the fields from the dictionary :(
fsouto
I wasn't returning the dictionary (I've updated the code above). You were getting "None" printed because the value wasn't being returned. Since this function doesn't modify the dictionary, you can simply print the same dictionary you passed in. I've updated the code so it returns the dict also.
Ned Batchelder
@Ned Batchelder - tbh I think your fisrt version was better, not returning the dictionary because as you said the original will already have the updated keys and you are not "wasting" the return value to return something already existent and the method could be modified in the future to return for example the number of values removed without changes to the already existent calling code.
laurent-rpnet
Oops!! I dont realize that missing return :( thanks for your code Ned!!
fsouto
@fsouto - I would use the actual dictionary modified for print (or other) statement and not the return value as when you use `print delete_keys...` you end having a print statement modifying data. Of course this is OK but personaly I avoid do do this because if you don't need anymore to print the dict, you can't only remove the print statement because the dictionary won't be updated so you'll have to remember that and comment it for other programers or for you in a few weeks and I think this is an unecessary potencial source of errors :)
laurent-rpnet
A: 

Since you already need to loop through every element in the dict, I'd stick with a single loop and just make sure to use a set for looking up the keys to delete

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
gnibbler