What is the proper way to remove keys from a dictionary with value == None
in Python?

- 20,879
- 9
- 40
- 61

- 2,778
- 5
- 26
- 40
-
6You'll have to clarify what you mean, as every key in a dictionary must have a value, even if that value is `''` or `0` or `None`. – TigerhawkT3 Nov 19 '15 at 06:57
-
1`{k: v for k, v in original.items() if v is not None}` – Shivam Jha Jul 01 '20 at 19:22
7 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

- 300,191
- 65
- 633
- 696
-
Currently the other two answers to this question appear to be iterating over the dictionary while deleting from it. Is that what you mentioned in the last sentence? Iterating over `dict.keys` or `dict.items`? – RobertL Nov 19 '15 at 23:05
-
1@RobertL -- Yeah. Iterating over `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 http://stackoverflow.com/a/6777632/748858 for example. – mgilson Nov 20 '15 at 01:24
-
Thanks for updating with Python 3 syntax. As Python 2 becomes older, this answer becomes more clumsy. Perhaps it's time to provide the Python 3+ answer first and the legacy syntax as a footnote. – Jason R. Coombs Aug 20 '18 at 17:26
-
1@JasonR.Coombs -- Agreed. I flip-flopped this one to default to python3.x -- Actually that would work on python2.x as well, it'd just be slightly less efficient. – mgilson Aug 20 '18 at 19:49
-
2
-
`ValueError: too many values to unpack (expected 2)`; it doesn't like the `k, v` part – Andrew Jun 22 '23 at 17:38
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}]}
}

- 3,117
- 2
- 15
- 23
-
2You need to add `list()` before `_dict.items()` to avoid raising a `RunTimeError` due to the dict changing size during iteration – Luca Feb 21 '21 at 20:52
-
1value can be list of dicts with None properties, add `elif isinstance(value, list): for v_i in value: delete_none(v_i)` – dave-cz Jul 21 '21 at 12:21
-
2There is no need to return _dict. This will only increase the runtime. _dict is passed by reference to the method. You can pass a copy of the dictionary you want to remove ones from to this function and get the result. – mightyMouse Aug 25 '21 at 18:57
-
1it is missing a case if you have an array on the top level: mylist: [ 1, 2,3 ] – Mandragor Dec 30 '21 at 10:27
-
@Mandragor good point, thanks, it can't be checked in the very beginning like: if not isinstance(_dict, dict): return _dict but your looks good – Vova Dec 31 '21 at 13:17
-
Could you clarify why this strategy is an advantage over using dictionary comprehensions (e.g. `{for k: v in k, v in d.items if v is not None}`? – SenhorLucas Jan 31 '22 at 11:30
-
-
@vova, with list/dictionary comprehension inside a dictionary comprehension. You can nest them. https://stackoverflow.com/questions/17915117/nested-dictionary-comprehension-python – SenhorLucas Nov 16 '22 at 15:19
For python 2.x
:
dict((k, v) for k, v in original.items() if v is not None)

- 1,791
- 2
- 17
- 44
-
1I can't speak for the down-voter, but one issue with the answer here is that `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`. – Rob Apr 05 '19 at 13:13
-
@Rob `is not` is not equivalent to `is` and `not` combined. It is a separate operator altogether that tests Identity. See `operator.is_not`for [Python2](https://docs.python.org/3/library/operator.html#operator.is_not) and [Python3](https://docs.python.org/2/library/operator.html#operator.is_not). – Amin Shah Gilani Nov 03 '21 at 19:27
-
1@AminShahGilani Yes, the answer has been edited. It was previously using `if not v` rather than `if v is not None`. Should I delete my comment now? – Rob Nov 06 '21 at 12:38
if you don't want to make a copy
for k,v in list(foo.items()):
if v is None:
del foo[k]

- 77
- 5
-
2
-
FYI, the `list` keyword creates a (shallow) copy of the key/value pairs. – Stefaan Ghysels Dec 20 '20 at 11:01
-
1
You could also take a copy of the dict
to avoid iterating the original dict
while altering it.
for k, v in dict(d).items():
if v is None:
del d[k]
But that might not be a great idea for larger dictionaries.

- 20,879
- 9
- 40
- 61
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

- 121
- 2
- 7
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'}]}}

- 447
- 1
- 6
- 13