2120

How do I delete an item from a dictionary in Python?

Without modifying the original dictionary, how do I obtain another dict with the item removed?


See also How can I remove a key from a Python dictionary? for the specific issue of removing an item (by key) that may not already be present.

Karl Knechtel
  • 62,466
  • 11
  • 102
  • 153
richzilla
  • 40,440
  • 14
  • 56
  • 86
  • 14
    Why do you need a function that returns a dictionary, when you can just modify the dictionary directly? – amillerrhodes May 01 '11 at 01:26
  • 10
    The [dictionary `pop` method](https://docs.python.org/2/library/stdtypes.html#dict.pop) 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. – Mark Mikofski Jul 01 '16 at 23:51
  • 1
    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 May 14 '18 at 22:59
  • 1
    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](https://stackoverflow.com/questions/2465921/how-to-copy-a-dictionary-and-only-edit-the-copy). 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?) – smci May 15 '18 at 01:13
  • 10
    "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? – Gene Callahan Jan 07 '19 at 04:39

19 Answers19

2459

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).

Vladimir Panteleev
  • 24,651
  • 6
  • 70
  • 114
Greg Hewgill
  • 951,095
  • 183
  • 1,149
  • 1,285
  • 21
    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. – tMC Apr 30 '11 at 21:38
  • 38
    @tMC If you edit the `dict` as you're looping through it, it'll give you an error: `RuntimeError: dictionary changed size during iteration` – VertigoRay Aug 29 '13 at 08:59
  • 25
    What about ```pop``` method which in fact does the same? Isn't it more pythonic? (being dict's method, not special reserved word)? – Serge Feb 17 '14 at 09:48
  • 27
    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) '. – Zen Jul 23 '14 at 10:34
  • @Zen: You're right, but that's an answer to a different question. This question was only asking about removing elements from a dictionary. – Greg Hewgill Jul 23 '14 at 19:18
  • 6
    @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. – Zen Jul 24 '14 at 03:09
  • 1
    To overcome `RuntimeError dictionary changed size during iteration` see: https://stackoverflow.com/a/11941855/4773274 – holzkohlengrill Jun 23 '17 at 14:20
  • 1
    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] – dmjalund Jun 27 '17 at 22:03
  • 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. – abarnert May 15 '18 at 01:29
  • @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` ? – DavidC. May 12 '20 at 08:49
  • @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. – BUFU Oct 01 '20 at 11:58
  • 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 – brookbot Mar 12 '22 at 07:30
  • @GregHewgill need **INFO** let's say I am adding an `item` to a `dict|HashMap|table` in each `iteration` inside a `fn` which is **O(n)** `space complexity` later on that `fn` I have another `iteration` which `removed` all `item` from the same `dict` will this be considered `space constant` ie **O(1)** ? – Azhar Uddin Sheikh Jan 12 '23 at 05:14
509

pop mutates the dictionary.

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

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

phoenix
  • 7,988
  • 6
  • 39
  • 45
Crystal
  • 5,960
  • 4
  • 21
  • 27
  • 91
    "del" is ok, but "pop" seems more "Pythonic", in my opinion. – ivanleoncz May 06 '17 at 02:27
  • 3
    @ivanleoncz why? – kevr Jan 23 '18 at 00:45
  • 6
    `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: https://github.com/ivanlmj/python-prototypes/blob/master/3.4/pop_list.py – ivanleoncz Jan 23 '18 at 00:59
  • 30
    @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. – itachi Mar 11 '20 at 01:56
117

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'}
utdemir
  • 26,532
  • 10
  • 62
  • 81
  • 3
    Really cool. I like the quick method to filter a dictionary without defining a new function. – Joe J Jul 16 '13 at 22:15
  • 8
    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. – kmatheny Feb 04 '15 at 20:01
  • 28
    Better would be `{k:v for k,v in a.items() if k != 0}` I think. – rlbond Feb 13 '17 at 23:01
  • 1
    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'})` – Cole Nov 19 '17 at 21:14
  • 1
    Best solution here. One liner and it doesn't mutate the original dictionary. – Andrew Winterbotham May 11 '18 at 13:52
109

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}
Nikita
  • 6,101
  • 2
  • 26
  • 44
81

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
arussell84
  • 2,443
  • 17
  • 18
  • 1
    @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 May 28 '17 at 20:16
  • 1
    @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... – maxkoryukov May 28 '17 at 20:19
  • @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 – pythonian29033 May 29 '17 at 09:22
  • @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 May 29 '17 at 09:44
  • 2
    @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? – maxkoryukov May 29 '17 at 09:56
  • 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 – pythonian29033 May 29 '17 at 10:33
  • 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 May 30 '17 at 14:15
  • 3
    I've added a function that can be copy/pasted, for your convenience. – arussell84 May 30 '17 at 14:28
49

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

A dict is the wrong data structure to use for this.

Sure, copying the dict and popping from the copy works, and so does building a new dict with a comprehension, but all that copying takes time—you've replaced a constant-time operation with a linear-time one. And all those copies alive at once take space—linear space per copy.

Other data structures, like hash array mapped tries, are designed for exactly this kind of use case: adding or removing an element returns a copy in logarithmic time, sharing most of its storage with the original.1

Of course there are some downsides. Performance is logarithmic rather than constant (although with a large base, usually 32-128). And, while you can make the non-mutating API identical to dict, the "mutating" API is obviously different. And, most of all, there's no HAMT batteries included with Python.2

The pyrsistent library is a pretty solid implementation of HAMT-based dict-replacements (and various other types) for Python. It even has a nifty evolver API for porting existing mutating code to persistent code as smoothly as possible. But if you want to be explicit about returning copies rather than mutating, you just use it like this:

>>> from pyrsistent import m
>>> d1 = m(a=1, b=2)
>>> d2 = d1.set('c', 3)
>>> d3 = d1.remove('a')
>>> d1
pmap({'a': 1, 'b': 2})
>>> d2
pmap({'c': 3, 'a': 1, 'b': 2})
>>> d3
pmap({'b': 2})

That d3 = d1.remove('a') is exactly what the question is asking for.

If you've got mutable data structures like dict and list embedded in the pmap, you'll still have aliasing issues—you can only fix that by going immutable all the way down, embedding pmaps and pvectors.


1. HAMTs have also become popular in languages like Scala, Clojure, Haskell because they play very nicely with lock-free programming and software transactional memory, but neither of those is very relevant in Python.

2. In fact, there is an HAMT in the stdlib, used in the implementation of contextvars. The earlier withdrawn PEP explains why. But this is a hidden implementation detail of the library, not a public collection type.

abarnert
  • 354,177
  • 51
  • 601
  • 671
24
d = {1: 2, '2': 3, 5: 7}
del d[5]
print 'd = ', d

Result: d = {1: 2, '2': 3}

Sociopath
  • 13,068
  • 19
  • 47
  • 75
satels
  • 789
  • 6
  • 13
24

Using del you can remove a dict value passing the key of that value

Link: del method

del dictionary['key_to_del']
Franz Kurt
  • 1,020
  • 2
  • 14
  • 14
17

Simply call del d['key'].

However, in production, it is always a good practice to check if 'key' exists in d.

if 'key' in d:
    del d['key']
Khanh Hua
  • 1,086
  • 1
  • 14
  • 22
  • 8
    Hmm, no, in production it is better to follow [EAFP](https://docs.python.org/2/glossary.html#term-eafp) ideology. Just remove key in `try-except` block. At least, this will be an atomic operation;) – maxkoryukov May 28 '17 at 20:09
  • 1
    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](https://stackoverflow.com/a/5844700/1115187) - is a good choice here;) – maxkoryukov May 28 '17 at 20:14
10
# mutate/remove with a default
ret_val = body.pop('key', 5)
# no mutation with a default
ret_val = body.get('key', 5)
daino3
  • 4,386
  • 37
  • 48
10

No, there is no other way than

def dictMinus(dct, val):
   copy = dct.copy()
   del copy[val]
   return copy

However, often creating copies of only slightly altered dictionaries is probably not a good idea because it will result in comparatively large memory demands. It is usually better to log the old dictionary(if even necessary) and then modify it.

phihag
  • 278,196
  • 72
  • 453
  • 469
7

Here a top level design approach:

def eraseElement(d,k):
    if isinstance(d, dict):
        if k in d:
            d.pop(k)
            print(d)
        else:
            print("Cannot find matching key")
    else:
        print("Not able to delete")


exp = {'A':34, 'B':55, 'C':87}
eraseElement(exp, 'C')

I'm passing the dictionary and the key I want into my function, validates if it's a dictionary and if the key is okay, and if both exist, removes the value from the dictionary and prints out the left-overs.

Output: {'B': 55, 'A': 34}

Hope that helps!

Jean-François Fabre
  • 137,073
  • 23
  • 153
  • 219
incalite
  • 3,077
  • 3
  • 26
  • 32
6

Below code snippet will help you definitely, I have added comments in each line which will help you in understanding the code.

def execute():
   dic = {'a':1,'b':2}
   dic2 = remove_key_from_dict(dic, 'b')  
   print(dict2)           # {'a': 1}
   print(dict)            # {'a':1,'b':2}

def remove_key_from_dict(dictionary_to_use, key_to_delete):
   copy_of_dict = dict(dictionary_to_use)     # creating clone/copy of the dictionary
   if key_to_delete in copy_of_dict :         # checking given key is present in the dictionary
       del copy_of_dict [key_to_delete]       # deleting the key from the dictionary 
   return copy_of_dict                        # returning the final dictionary

or you can also use dict.pop()

d = {"a": 1, "b": 2}

res = d.pop("c")  # No `KeyError` here
print (res)       # this line will not execute

or the better approach is

res = d.pop("c", "key not found")
print (res)   # key not found
print (d)     # {"a": 1, "b": 2}

res = d.pop("b", "key not found")
print (res)   # 2
print (d)     # {"a": 1}
Mayur Agarwal
  • 869
  • 10
  • 28
6
>>> def delete_key(dict, key):
...     del dict[key]
...     return dict
... 
>>> test_dict = {'one': 1, 'two' : 2}
>>> print delete_key(test_dict, 'two')
{'one': 1}
>>>

this doesn't do any error handling, it assumes the key is in the dict, you might want to check that first and raise if its not

tMC
  • 18,105
  • 14
  • 62
  • 98
5

Solution 1: with deleting

info = {'country': 'Iran'}
country = info.pop('country') if 'country' in info else None

Solution 2: without deleting

info = {'country': 'Iran'}
country = info.get('country') or None
Mohammad Reza
  • 693
  • 9
  • 16
3

Here's another variation using list comprehension:

original_d = {'a': None, 'b': 'Some'}
d = dict((k,v) for k, v in original_d.iteritems() if v)
# result should be {'b': 'Some'}

The approach is based on an answer from this post: Efficient way to remove keys with empty strings from a dict

For Python 3 this is

original_d = {'a': None, 'b': 'Some'}
d = dict((k,v) for k, v in original_d.items() if v)
print(d)
KansaiRobot
  • 7,564
  • 11
  • 71
  • 150
BigBlueHat
  • 2,355
  • 25
  • 30
  • 1
    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. – user2357112 Aug 30 '13 at 01:12
  • 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" http://stackoverflow.com/questions/12118695/efficient-way-to-remove-keys-with-empty-values-from-a-dict#comment16203889_12118700 I knew it wasn't a direct answer to the question; rather an expansion to the options. – BigBlueHat Aug 30 '13 at 01:49
  • 3
    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. – holgac May 10 '14 at 14:24
  • @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. – Phil Sep 08 '20 at 12:25
  • AttributeError: 'dict' object has no attribute 'iteritems' – KansaiRobot Mar 04 '22 at 03:09
2
    species = {'HI': {'1': (1215.671, 0.41600000000000004),
  '10': (919.351, 0.0012),
  '1025': (1025.722, 0.0791),
  '11': (918.129, 0.0009199999999999999),
  '12': (917.181, 0.000723),
  '1215': (1215.671, 0.41600000000000004),
  '13': (916.429, 0.0005769999999999999),
  '14': (915.824, 0.000468),
  '15': (915.329, 0.00038500000000000003),
 'CII': {'1036': (1036.3367, 0.11900000000000001), '1334': (1334.532, 0.129)}}

The following code will make a copy of dict species and delete items which are not in trans_HI

trans_HI=['1025','1215']
for transition in species['HI'].copy().keys():
    if transition not in trans_HI:
        species['HI'].pop(transition)
Sameeresque
  • 2,464
  • 1
  • 9
  • 22
0

In Python 3, 'dict' object has no attribute 'remove'.

But with immutables package, can perform mutations that allow to apply changes to the Map object and create new (derived) Maps:

import immutables
map = immutables.Map(a=1, b=2)
map1 = map.delete('b')
print(map, map1)
# will print:
#   <immutables.Map({'b': 2, 'a': 1})>
#   <immutables.Map({'a': 1})>
DOT
  • 309
  • 2
  • 11
-1

can try my method. In one line.

yourList = [{'key':'key1','version':'1'},{'key':'key2','version':'2'},{'key':'key3','version':'3'}]
resultList = [{'key':dic['key']} for dic in yourList if 'key' in dic]
print(resultList)
Vincent55
  • 33
  • 3