What is the proper way to remove keys from a dictionary with value == None in Python?
8 Answers
Generally, you'll create a new dict constructed from filtering the old one. dictionary comprehensions are great for this sort of thing:
{k: v for k, v in original.items() if v is not None}
If you must update the original dict, you can do it like this ...
filtered = {k: v for k, v in original.items() if v is not None}
original.clear()
original.update(filtered)
This is probably the most "clean" way to remove them in-place that I can think of (it isn't safe to modify a dict while you're iterating over it)
Use original.iteritems() on python2.x
6 Comments
dict.keys or dict.items?dict is always unsafe if you are going to be mutating the dictionary in the process. Iterating over dict.keys() and dict.items() is fine on python2.x, but unsafe on python3.x. See stackoverflow.com/a/6777632/748858 for example.if you need to delete None values recursively, better to use this one:
def delete_none(_dict):
"""Delete None values recursively from all of the dictionaries"""
for key, value in list(_dict.items()):
if isinstance(value, dict):
delete_none(value)
elif value is None:
del _dict[key]
elif isinstance(value, list):
for v_i in value:
if isinstance(v_i, dict):
delete_none(v_i)
return _dict
with advice of @dave-cz, there was added functionality to support values in list type.
@mandragor added additional if statement to allow dictionaries which contain simple lists.
Here's also solution if you need to remove all of the None values from dictionaries, lists, tuple, sets:
def delete_none(_dict):
"""Delete None values recursively from all of the dictionaries, tuples, lists, sets"""
if isinstance(_dict, dict):
for key, value in list(_dict.items()):
if isinstance(value, (list, dict, tuple, set)):
_dict[key] = delete_none(value)
elif value is None or key is None:
del _dict[key]
elif isinstance(_dict, (list, set, tuple)):
_dict = type(_dict)(delete_none(item) for item in _dict if item is not None)
return _dict
The result is:
# passed:
a = {
"a": 12, "b": 34, "c": None,
"k": {"d": 34, "t": None, "m": [{"k": 23, "t": None},[None, 1, 2, 3],{1, 2, None}], None: 123}
}
# returned:
a = {
"a": 12, "b": 34,
"k": {"d": 34, "m": [{"k": 23}, [1, 2, 3], {1, 2}]}
}
8 Comments
list() before _dict.items() to avoid raising a RunTimeError due to the dict changing size during iterationelif isinstance(value, list): for v_i in value: delete_none(v_i)if you don't want to make a copy
for k,v in list(foo.items()):
if v is None:
del foo[k]
3 Comments
foo.pop(k) else it'll faillist keyword creates a (shallow) copy of the key/value pairs.For python 2.x:
dict((k, v) for k, v in original.items() if v is not None)
3 Comments
not v will evalute to True if bool(v) evaluates to False. This is the case for v == '' (empty string), for example, which is different from saying v == None.if not v rather than if v is not None. Should I delete my comment now?Python3 recursive version
def drop_nones_inplace(d: dict) -> dict:
"""Recursively drop Nones in dict d in-place and return original dict"""
dd = drop_nones(d)
d.clear()
d.update(dd)
return d
def drop_nones(d: dict) -> dict:
"""Recursively drop Nones in dict d and return a new dict"""
dd = {}
for k, v in d.items():
if isinstance(v, dict):
dd[k] = drop_nones(v)
elif isinstance(v, (list, set, tuple)):
# note: Nones in lists are not dropped
# simply add "if vv is not None" at the end if required
dd[k] = type(v)(drop_nones(vv) if isinstance(vv, dict) else vv
for vv in v)
elif v is not None:
dd[k] = v
return dd
Comments
Here is a recursive function returning a new clean dictionary without keys with None values:
def clean_dict(d):
clean = {}
for key, value in d.items():
if value is not None:
if isinstance(value, dict):
subdict = clean_dict(value)
if subdict:
clean[key] = subdict
else:
clean[key] = value
return clean
example = {
"a": 12, "b": "", "c": None, "d": {"e": {"f": None}},
"k": {"d": 34, "t": None, "m": {"k": [], "t": {"x": 0}}, None: 123}
}
print(clean_dict(example))
Output is
{'a': 12, 'b': '', 'k': {'d': 34, 'm': {'k': [], 't': {'x': 0}}, None: 123}}
Note that it also removed "d": {"e": {"f": None}} from the result.
Comments
Maybe you'll find it useful:
def clear_dict(d):
if d is None:
return None
elif isinstance(d, list):
return list(filter(lambda x: x is not None, map(clear_dict, d)))
elif not isinstance(d, dict):
return d
else:
r = dict(
filter(lambda x: x[1] is not None,
map(lambda x: (x[0], clear_dict(x[1])),
d.items())))
if not bool(r):
return None
return r
it would:
clear_dict(
{'a': 'b', 'c': {'d': [{'e': None}, {'f': 'g', 'h': None}]}}
)
->
{'a': 'b', 'c': {'d': [{'f': 'g'}]}}
''or0orNone.{k: v for k, v in original.items() if v is not None}