How to remove empty or None fields in deeply nested dictionary of unknown depth?

Viewed 473

I have a dictionary of deeply nested dictionaries and I am trying to remove all k-v pairs that are None or "". The below dictionary d is an example of the input.

d = { 
  "1": {
    "1_1": 'a real value',
    "1_2": ""
    },
   "2": None,
   "3": {
     "3_1": {
       "3_2": None
     }
   }
}

Normally, to remove empty fields in a dictionary, the command {k:v for k,v in d.items() if v} does the job. But in this case, I want to remove all the nested dictionaries whose values are empty or null, and I can't figure out how to do this. Any ideas?

After d passes through this transformation, all the empty dictionaries whose values are empty should be gone. It should look like this:

{ 
  "1": {
    "1_1": 'a real value',
    }
}
3 Answers

You can write a function to recursively remove empty dictionaries. For example:

def return_non_empty(my_dict):
    temp_dict = {}
    for k, v in my_dict.items():
        if v:
            if isinstance(v, dict):
                 return_dict = return_non_empty(v)
                 if return_dict:
                     temp_dict[k] = return_dict
            else:
                temp_dict[k] = v
    return temp_dict

For example, if d is:

d = { 
  "1": {
    "1_1": 'a real value',
    "1_2": ""
    },
   "2": None,
   "3": {
     "3_1": {
       "3_2": None
     }
   }
}

then my custom function return_non_empty will return:

>>> return_non_empty(d)
{'1': {'1_1': 'a real value'}}

You can use recursion with a dictionary comprehension:

d = {'1': {'1_1': 'a real value', '1_2': ''}, '2': None, '3': {'3_1': {'3_2': None}}}
def get_d(d):
  return {a:c for a, b in d.items() if (c:=(b if not isinstance(b, dict) else get_d(b)))}

print(get_d(d))

Output:

{'1': {'1_1': 'a real value'}}

Note: this solution uses Python's assignment expressions (:=) available in versions >= 3.8. For a solution that does not use this paradigm, please see @Anonymous's answer.

This python function recursively removes dictionary keys whose values are empty, None, False, or 0.

def remove_empty_keys(dictionary):
    if isinstance(dictionary, dict):
        for key, value in dictionary.copy().items():
            if isinstance(value, dict):
                remove_empty_keys(value)
            if not value:
                del(dictionary[key])

Similar to @Ajax1234's answer but this function acts directly on target dictionary rather than outputting a new one.

Related