Newboy11
Newboy11

Reputation: 3136

Proper way to remove keys in dictionary with None values in Python

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

Upvotes: 127

Views: 132427

Answers (8)

asmaier
asmaier

Reputation: 11746

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.

Upvotes: 0

Vova
Vova

Reputation: 3543

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}]}
}

Upvotes: 21

Stefaan Ghysels
Stefaan Ghysels

Reputation: 131

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

Upvotes: 2

JSBach
JSBach

Reputation: 446

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

Upvotes: -1

Veaceslav Mindru
Veaceslav Mindru

Reputation: 97

if you don't want to make a copy

for k,v  in list(foo.items()):
   if v is None:
      del foo[k]

Upvotes: 5

user2340939
user2340939

Reputation: 1981

For python 2.x:

dict((k, v) for k, v in original.items() if v is not None)

Upvotes: 4

mgilson
mgilson

Reputation: 309821

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

Upvotes: 211

Paul Rooney
Paul Rooney

Reputation: 21609

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.

Upvotes: 3

Related Questions