71

I want to make a function that returns a copy of a dictionary excluding keys specified in a list.

Considering this dictionary:

my_dict = {
    "keyA": 1,
    "keyB": 2,
    "keyC": 3
}

A call to without_keys(my_dict, ['keyB', 'keyC']) should return:

{
    "keyA": 1
}

I would like to do this in a one-line with a neat dictionary comprehension but I'm having trouble. My attempt is this:

def without_keys(d, keys):
    return {k: d[f] if k not in keys for f in d}

which is invalid syntax. How can I do this?

dreftymac
  • 31,404
  • 26
  • 119
  • 182
Juicy
  • 11,840
  • 35
  • 123
  • 212

7 Answers7

77

You were close, try the snippet below:

>>> my_dict = {
...     "keyA": 1,
...     "keyB": 2,
...     "keyC": 3
... }
>>> invalid = {"keyA", "keyB"}
>>> def without_keys(d, keys):
...     return {x: d[x] for x in d if x not in keys}
>>> without_keys(my_dict, invalid)
{'keyC': 3}

Basically, the if k not in keys will go at the end of the dict comprehension in the above case.

Anshul Goyal
  • 73,278
  • 37
  • 149
  • 186
  • 1
    You declared invalid as a set. Is there a reason for that? Would a list suffice as well? – Prithvi Boinpally Oct 23 '20 at 03:10
  • 2
    Yes, list will suffice as well (you can try it), but generally when order is not important for you, `set` is preferred (even from performance side which is not important in this short example with just a few data). – Nerxis Dec 10 '20 at 09:58
  • Is this faster than getting all keys and then manually skipping not needed keys with a for loop? – sebko_iic Dec 24 '20 at 15:16
43

In your dictionary comprehension you should be iterating over your dictionary (not k , not sure what that is either). Example -

return {k:v for k,v in d.items() if k not in keys}
Anand S Kumar
  • 88,551
  • 18
  • 188
  • 176
10

This should work for you.

def without_keys(d, keys):
    return {k: v for k, v in d.items() if k not in keys}
Morgan Thrapp
  • 9,748
  • 3
  • 46
  • 67
9

Even shorter. Apparently python 3 lets you 'subtract' a list from a dict_keys.

def without_keys(d, keys):
    return {k: d[k] for k in d.keys() - keys}
Mike Fogel
  • 3,127
  • 28
  • 22
  • 5
    Note that using `keys()` instead of `items()` is ~33% slower: https://discuss.python.org/t/copy-a-dictionary-except-some-keys/2559/5 – Tuukka Mustonen Dec 16 '20 at 10:39
1

For those who don't like list comprehensions, this is my version:

def without_keys(d, *keys):
     return dict(filter(lambda key_value: key_value[0] not in keys, d.items()))

Usage:

>>> d={1:3, 5:7, 9:11, 13:15}
>>> without_keys(d, 1, 5, 9)
{13: 15}
>>> without_keys(d, 13)
{1: 3, 5: 7, 9: 11}
>>> without_keys(d, *[5, 7])
{1: 3, 13: 15, 9: 11}
Xiv
  • 8,862
  • 3
  • 27
  • 30
Himel Das
  • 1,148
  • 10
  • 13
  • 1
    For those who don't like list comprehensions, you should learn list comprehensions. They are very 'pythonic', and we they eventually click, you will be gald you spent the time to understand them, or else you are fighting the language. – run_the_race Sep 19 '22 at 18:21
1

Your oneliner

my_dict = {"keyA": 1, "keyB": 2, "keyC": 3}
(lambda keyB, keyC, **kw: kw)(**my_dict)

which returns {'keyA': 1}. Not very pythonic and dynamic, but hacky and short. It uses the dict unpacking (destructuring assignment) of function arguments.

See also https://stackoverflow.com/a/53851069/11769765.

0

You could this generalized for nested dictionaries solution

def copy_dict(data, strip_values=False, remove_keys=[]):
    if type(data) is dict:
        out = {}
        for key, value in data.items():
            if key not in remove_keys:
                out[key] = copy_dict(value, strip_values=strip_values, remove_keys=remove_keys)
        return out
    else:
        return [] if strip_values else data

This recursive solution works for nested dictionaries and removes keys not required from the entire nested structure. It also gives you the ability to return the nest with only keys and no values.

Rakshit Kothari
  • 399
  • 1
  • 5
  • 16