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'}]}}
''
or0
orNone
.{k: v for k, v in original.items() if v is not None}