Delete an element from a dictionary

2,309,449

Solution 1

The del statement removes an element:

del d[key]

Note that this mutates the existing dictionary, so the contents of the dictionary changes for anybody else who has a reference to the same instance. To return a new dictionary, make a copy of the dictionary:

def removekey(d, key):
    r = dict(d)
    del r[key]
    return r

The dict() constructor makes a shallow copy. To make a deep copy, see the copy module.


Note that making a copy for every dict del/assignment/etc. means you're going from constant time to linear time, and also using linear space. For small dicts, this is not a problem. But if you're planning to make lots of copies of large dicts, you probably want a different data structure, like a HAMT (as described in this answer).

Solution 2

pop mutates the dictionary.

 >>> lol = {"hello": "gdbye"}
 >>> lol.pop("hello")
     'gdbye'
 >>> lol
     {}

If you want to keep the original you could just copy it.

Solution 3

I think your solution is best way to do it. But if you want another solution, you can create a new dictionary with using the keys from old dictionary without including your specified key, like this:

>>> a
{0: 'zero', 1: 'one', 2: 'two', 3: 'three'}
>>> {i:a[i] for i in a if i!=0}
{1: 'one', 2: 'two', 3: 'three'}

Solution 4

There're a lot of nice answers, but I want to emphasize one thing.

You can use both dict.pop() method and a more generic del statement to remove items from a dictionary. They both mutate the original dictionary, so you need to make a copy (see details below).

And both of them will raise a KeyError if the key you're providing to them is not present in the dictionary:

key_to_remove = "c"
d = {"a": 1, "b": 2}
del d[key_to_remove]  # Raises `KeyError: 'c'`

and

key_to_remove = "c"
d = {"a": 1, "b": 2}
d.pop(key_to_remove)  # Raises `KeyError: 'c'`

You have to take care of this:

by capturing the exception:

key_to_remove = "c"
d = {"a": 1, "b": 2}
try:
    del d[key_to_remove]
except KeyError as ex:
    print("No such key: '%s'" % ex.message)

and

key_to_remove = "c"
d = {"a": 1, "b": 2}
try:
    d.pop(key_to_remove)
except KeyError as ex:
    print("No such key: '%s'" % ex.message)

by performing a check:

key_to_remove = "c"
d = {"a": 1, "b": 2}
if key_to_remove in d:
    del d[key_to_remove]

and

key_to_remove = "c"
d = {"a": 1, "b": 2}
if key_to_remove in d:
    d.pop(key_to_remove)

but with pop() there's also a much more concise way - provide the default return value:

key_to_remove = "c"
d = {"a": 1, "b": 2}
d.pop(key_to_remove, None)  # No `KeyError` here

Unless you use pop() to get the value of a key being removed you may provide anything, not necessary None. Though it might be that using del with in check is slightly faster due to pop() being a function with its own complications causing overhead. Usually it's not the case, so pop() with default value is good enough.


As for the main question, you'll have to make a copy of your dictionary, to save the original dictionary and have a new one without the key being removed.

Some other people here suggest making a full (deep) copy with copy.deepcopy(), which might be an overkill, a "normal" (shallow) copy, using copy.copy() or dict.copy(), might be enough. The dictionary keeps a reference to the object as a value for a key. So when you remove a key from a dictionary this reference is removed, not the object being referenced. The object itself may be removed later automatically by the garbage collector, if there're no other references for it in the memory. Making a deep copy requires more calculations compared to shallow copy, so it decreases code performance by making the copy, wasting memory and providing more work to the GC, sometimes shallow copy is enough.

However, if you have mutable objects as dictionary values and plan to modify them later in the returned dictionary without the key, you have to make a deep copy.

With shallow copy:

def get_dict_wo_key(dictionary, key):
    """Returns a **shallow** copy of the dictionary without a key."""
    _dict = dictionary.copy()
    _dict.pop(key, None)
    return _dict


d = {"a": [1, 2, 3], "b": 2, "c": 3}
key_to_remove = "c"

new_d = get_dict_wo_key(d, key_to_remove)
print(d)  # {"a": [1, 2, 3], "b": 2, "c": 3}
print(new_d)  # {"a": [1, 2, 3], "b": 2}
new_d["a"].append(100)
print(d)  # {"a": [1, 2, 3, 100], "b": 2, "c": 3}
print(new_d)  # {"a": [1, 2, 3, 100], "b": 2}
new_d["b"] = 2222
print(d)  # {"a": [1, 2, 3, 100], "b": 2, "c": 3}
print(new_d)  # {"a": [1, 2, 3, 100], "b": 2222}

With deep copy:

from copy import deepcopy


def get_dict_wo_key(dictionary, key):
    """Returns a **deep** copy of the dictionary without a key."""
    _dict = deepcopy(dictionary)
    _dict.pop(key, None)
    return _dict


d = {"a": [1, 2, 3], "b": 2, "c": 3}
key_to_remove = "c"

new_d = get_dict_wo_key(d, key_to_remove)
print(d)  # {"a": [1, 2, 3], "b": 2, "c": 3}
print(new_d)  # {"a": [1, 2, 3], "b": 2}
new_d["a"].append(100)
print(d)  # {"a": [1, 2, 3], "b": 2, "c": 3}
print(new_d)  # {"a": [1, 2, 3, 100], "b": 2}
new_d["b"] = 2222
print(d)  # {"a": [1, 2, 3], "b": 2, "c": 3}
print(new_d)  # {"a": [1, 2, 3, 100], "b": 2222}

Solution 5

The del statement is what you're looking for. If you have a dictionary named foo with a key called 'bar', you can delete 'bar' from foo like this:

del foo['bar']

Note that this permanently modifies the dictionary being operated on. If you want to keep the original dictionary, you'll have to create a copy beforehand:

>>> foo = {'bar': 'baz'}
>>> fu = dict(foo)
>>> del foo['bar']
>>> print foo
{}
>>> print fu
{'bar': 'baz'}

The dict call makes a shallow copy. If you want a deep copy, use copy.deepcopy.

Here's a method you can copy & paste, for your convenience:

def minus_key(key, dictionary):
    shallow_copy = dict(dictionary)
    del shallow_copy[key]
    return shallow_copy
Share:
2,309,449
richzilla
Author by

richzilla

Updated on July 23, 2022

Comments

  • richzilla
    richzilla almost 2 years

    Is there a way to delete an item from a dictionary in Python?

    Additionally, how can I delete an item from a dictionary to return a copy (i.e., not modifying the original)?

    • amillerrhodes
      amillerrhodes about 13 years
      Why do you need a function that returns a dictionary, when you can just modify the dictionary directly?
    • Mark Mikofski
      Mark Mikofski about 8 years
      The dictionary pop method changes the dictionary in-place. Therefore it alters the reference to the dictionary that was passed from the caller to the "helper function". So the "helper function" doesn't need to return anything, since the original reference to the dictionary in the caller will already be altered. Don't assign the return from dict.pop() to anything if you don't need it. EG: do stuff with my_dict; my_dict.pop(my_key, None); do more stuff with my_dict # now doesn't have my_key. Use deepcopy(my_dict) if needed.
    • smci
      smci about 6 years
      Since the original title disagreed with the details and specifically excluded the obvious solution d.pop(), I fixed the title to ask the question specified in the details.
    • smci
      smci about 6 years
      We should add a caveat asking if you really want to do this, as if you do it N times on a dictionary with E elements you'll leak(/use) O(N*E) memory with all the deep copies. If you merely want a read-only (shallow copy), do d.pop(key). But if anything ever modifies the shallow copy, you have a well-known problem with aliasing. It helps if you tell us the wider context. (Is anything else ever modifying the dict values? Are you trying to destructively iterate over a list? if not, what?)
    • Gene Callahan
      Gene Callahan over 5 years
      "Why do you need a function that returns a dictionary, when you can just modify the dictionary directly?" Perhaps because you want to write pure functions that don't modify their parameters?
  • tMC
    tMC about 13 years
    thats a great point about the mutability of dictionaries +1 - though i can't think of a time when i wanted copies of the dictionary, i've always relied on 'everyones' copy being the same. great point.
  • Joe J
    Joe J almost 11 years
    Really cool. I like the quick method to filter a dictionary without defining a new function.
  • VertigoRay
    VertigoRay almost 11 years
    @tMC If you edit the dict as you're looping through it, it'll give you an error: RuntimeError: dictionary changed size during iteration
  • user2357112
    user2357112 almost 11 years
    If you're going to answer a years-old question that already has a simple, appropriate, accepted answer, at least make sure your answer is right. This doesn't do what the OP asked for.
  • BigBlueHat
    BigBlueHat almost 11 years
    I don't generally check dates on questions I think could have valuable info added to them. Additionally, per one of the comments on the question I linked to: "Usually this is exactly what someone wants and is probably what the OP needs, but it is not what the OP asked for" stackoverflow.com/questions/12118695/… I knew it wasn't a direct answer to the question; rather an expansion to the options.
  • Serge
    Serge over 10 years
    What about pop method which in fact does the same? Isn't it more pythonic? (being dict's method, not special reserved word)?
  • holgac
    holgac about 10 years
    This answer, although not complete, lets us learn that we can remove items with if conditions too. just changing if v to if k is not 'a' answers the op. But i don't think that's an efficient way, this removes the element in O(n) rather than O(log n) as pop or del does.
  • Zen
    Zen almost 10 years
    This answer has a weakness, it could be misleading. Readers may misunderstand that dict(d) can give them a copy with 'd'. But it's an incomplete copy. When only doing del keys operations, that's OK. But when you want to do something else to a nested dict, modifying 'r' using that copy method may cause change to the original 'd'. To get an authentic copy, you need first ' import copy ', and then 'r = copy.deepcopy(d) '.
  • Greg Hewgill
    Greg Hewgill almost 10 years
    @Zen: You're right, but that's an answer to a different question. This question was only asking about removing elements from a dictionary.
  • Zen
    Zen almost 10 years
    @GregHewgill, I know that, so it's not a serious weakness. But since you mentioned "To return a new dictionary, make a copy of the dictionary:". With your 358K points and 143095 view times, this could be misleading to considerable beginners of python. By the way, I myself was mislead by this post before.
  • kmatheny
    kmatheny over 9 years
    For those not familiar with comprehensions, you can also do something like this: {i:a[i] for i in a if i not in [0, 1, 2]} if you want to remove several elements.
  • Mad Physicist
    Mad Physicist over 8 years
    How is your method any different than just del test_dict[key]?
  • rlbond
    rlbond over 7 years
    Better would be {k:v for k,v in a.items() if k != 0} I think.
  • ivanleoncz
    ivanleoncz about 7 years
    "del" is ok, but "pop" seems more "Pythonic", in my opinion.
  • maxkoryukov
    maxkoryukov about 7 years
    Hmm, no, in production it is better to follow EAFP ideology. Just remove key in try-except block. At least, this will be an atomic operation;)
  • maxkoryukov
    maxkoryukov about 7 years
    And if you want to be concise — use d.pop('key', None) , it is oneliner. But the actual question was about getting the dictionary without one key, and not about modifying the dict. So comprehensions - is a good choice here;)
  • maxkoryukov
    maxkoryukov about 7 years
    @pythonian29033 , actually, no. The accepted answer works as expected — it returns the dict without one key. The approach from this answer mutates original dict;) There is a significat difference
  • maxkoryukov
    maxkoryukov about 7 years
    @arussell84, why >>> is often used in the python-examples? Yeah, python-doc contains a lot of such things. But such code is not convenient for copypaste. I am confused...
  • pythonian29033
    pythonian29033 about 7 years
    @maxkoryukov yup it does! but that function and this answer is exactly the same, with the exception of that answer being inside of a function. and you must not have been coding in python for a while, the >>> mimics the listening notation from python in cli mode
  • maxkoryukov
    maxkoryukov about 7 years
    @pythonian29033, agreed;) sorry, my mistake, the code from this answer actually do the same job as the accepted one. You are right about this;)
  • maxkoryukov
    maxkoryukov about 7 years
    @pythonian29033 about >>>. Yes, it is REPL-style, but let's talk frankly: the only one man had wrote this sample, and 1000 have read this. I think, it would be great to write examples in the way allowing easy copy and run. I don't like to remove this angle brackets by hand. Or copy line by line.. So I don't understand: why this angles are still there))) May be I don't know something?
  • pythonian29033
    pythonian29033 about 7 years
    well I guess guys are posting their answers with the intention of solving the problems and helping understand the solution as apposed to something that can just be copied and pasted
  • arussell84
    arussell84 about 7 years
    Yes, I used the REPL-style syntax to illustrate what is actual code vs. what the output is. I wanted to show the output to better illustrate that the original and the (shallow) copy are different after the dict statement.
  • arussell84
    arussell84 about 7 years
    I've added a function that can be copy/pasted, for your convenience.
  • holzkohlengrill
    holzkohlengrill about 7 years
    To overcome RuntimeError dictionary changed size during iteration see: stackoverflow.com/a/11941855/4773274
  • dmjalund
    dmjalund about 7 years
    to go through items in a dictionary to delete, I first create a list of the keys of the records i need to delete, then I iterate through this list and do del mydict[iterKey]
  • Cole
    Cole over 6 years
    The best solution for removing an item by key and returning the result of the new dict in the same line. For example if you need to use an an already constructed dict without a single item as **kwargs, some_function(**{k:v for k,v in some_dict.items() if k not 'some_key'})
  • kevr
    kevr over 6 years
    @ivanleoncz why?
  • ivanleoncz
    ivanleoncz over 6 years
    pop returns the value that was 'popped', which allows you to use this value for whatever further reason. If it's not more "Pythonic", I'd say that seems better, for sure :). It's not a dict, but it works the same way for both: github.com/ivanlmj/python-prototypes/blob/master/3.4/…
  • Andrew Winterbotham
    Andrew Winterbotham about 6 years
    Best solution here. One liner and it doesn't mutate the original dictionary.
  • abarnert
    abarnert about 6 years
    I've added an explanation of the costs of copying a dict every time you want to change it, with a link to my answer that shows how to avoid them. (This is definitely not what you want to do all the time, but I think it is what some people looking for this question will, or should, want to do.) Feel free to revert if that seems inappropriate.
  • itachi
    itachi over 4 years
    @ivanleoncz It's also better for one more reason, pop can be provided with a default value that will be returned when a key is missing from dict. It's good when you need to remove a few keys but some of them might be missing; del would throw KeyError in such case.
  • DavidC.
    DavidC. about 4 years
    @Greg Hewgill Thank you for your answer. I was wondering if a simpler solution is to copy the d dictionary to d_copy: d = d_copy and then work over d_copy. What is the difference between this approach with respect to making a shallow copy (dict() constructor) or even making a deep copy ?
  • Phil
    Phil almost 4 years
    @holgac copying the dictionary runs in O(n), so your argument about efficiency is not correct given OP required the original dict remain unmodified, which requires the dict be copied.
  • BUFU
    BUFU almost 4 years
    @DavidC. when you d_copy = d, you reference the same dictionary with a second name (so the id() of both is the same). when you d_copy = dict(d) you make a (shallow) copy and therefore another dict with the same content. so when you del in the first version, both d and d_copy are altered, in the second, they are not - unless you have a nested dict or something (like a list inside the dict), because the nested dict/list is the same for both again (because the content is another reference to the same dict/list)... and that's where copy.deepcopy() comes in.
  • KansaiRobot
    KansaiRobot over 2 years
    AttributeError: 'dict' object has no attribute 'iteritems'
  • brookbot
    brookbot over 2 years
    The answer fails to mention that a KeyError exception may be raised and needs to be handled if the key is missing. Up voted nikita answer below