Key error in dictionary while deleting the key

Viewed 1640

I am trying to count the string and I want to delete the key with value 1 and 2. I am successfully able to count the strings with below script. If I am adding if loop to the dictionary its throwing Key error 'd' when I try to print it.

s = "aaabbbd"
def check_freq(s):
     freq = {}
     for c in s:
        freq[c] = s.count(c)
     for w in sorted(freq, key=freq.get, reverse=True):
         if freq[w] == 1:
             del freq[w]
             print (w, freq[w])

check_freq(s)

Error is below

File "<ipython-input-60-a79c71ac1b31>", line 10, in check_freq
    print (w, freq[w])

KeyError: 'd'

Expected output

{'a': 3, 'b': 3}

6 Answers

If you delete the key, how can you print it? Because the key is deleted, it raises a KeyError as the key is no longer present. Just add a continue when you delete it.

s = "aaabbbd"
def check_freq(s):
    freq = {}
    for c in s:
        freq[c] = s.count(c)
    for w in sorted(freq, key=freq.get, reverse=True):
        if freq[w] == 1:
            del freq[w]
            continue
        print (w, freq[w])
    return freq
result = check_freq(s)

>a 3
b 3

result 
> {'a': 3, 'b': 3}

The KeyError is happening because when you write

if freq[w] == 1:
    del freq[w]
    print (w, freq[w])

for w = 'd', freq[w] = 1. So, you are deleting freq[w] but in the next line, you are trying to print freq[w] which doesn't exist. You could avoid this by using an if-else clause.

s = "aaabbbd"
def check_freq(s):
    freq = {}
    for c in s:
        freq[c] = s.count(c)
    for w in sorted(freq, key=freq.get, reverse=True):
        if freq[w] == 1 or freq[w] == 2:
            del freq[w]
        else:
            print (w, freq[w])
print(freq)

check_freq(s)

Output:

a 3
b 3
{'a': 3, 'b': 3}

By deleting the entry from the freq dictionary you cannot print it anymore; it doesn't exist. You could add an else clausule to prevent this:

 for w in sorted(freq, key=freq.get, reverse=True):
     if freq[w] == 1:
         del freq[w]
     else:
         print (w, freq[w])

You could try this:

s = "aaabbbd"

def check_freq(s):
    freq = {}
    for c in s:
        freq[c] = s.count(c)

    for key, value in freq.items():
        if value == 1:
            del freq[key]

    return freq

print check_freq(s)

Output:

{'a': 3, 'b': 3}

Actually you don't need to sort the dictionary. You just need to iterate through its elements and check whether the key is equal to one. Then delete this key-value pair from the dictionary.

Keep delete in try block and that worked for me.

try:
   del freq[w]
except Exception as e:
   print(e)

Instead of deleting keys, you could always rebuild a new dictionary with only keep keys with values greater than 2:

from collections import Counter

s = "aaabbbd"

counts = {k: v for k, v in Counter(s).items() if v > 2}

print(counts)
# {'a': 3, 'b': 3}

Which also uses collections.Counter() to construct the counts. Using s.count() is very inefficient since it scans the whole string each time to count a character. This ends up being quadratic O(N^2), since it has to visit every character for each character. Counter() simply counts all the characters in one traversal, which is O(N).

If you want to delete from the same dictionary, you can iterate over a copy and delete from the original with dict.pop:

from collections import Counter

s = "aaabbbd"

counts = Counter(s)

count_items = list(counts.items())
for k, v in count_items:
    if v <= 2:
        counts.pop(k) # Or del counts[k]

print(counts)
# Counter({'a': 3, 'b': 3})

Note: Counter() is a subclass of dict, so you can treat them the same. Having said this, one of the differences is that Counter() doesn't raise KeyError when a key doesn't exist, it returns 0 instead.

This can be demonstrated here:

>>> from collections import Counter
>>> x = Counter({'a': 1})
>>> y = {'a': 1}
>>> x['b']
0
>>> y['b']
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: 'b'
Related