This question already has answers here:
Removing entries from a dictionary based on values
(4 answers)
Closed 9 years ago.
I know dictionary's are not meant to be used this way, so there is no built in function to help do this, but I need to delete every entry in my dictionary that has a specific value.
so if my dictionary looks like:
'NameofEntry1': '0'
'NameofEntry2': 'DNC'
...
I need to delete(probably pop) all the entries that have value DNC, there are multiple in the dictionary.
Modifying the original dict:
for k,v in your_dict.items():
if v == 'DNC':
del your_dict[k]
or create a new dict using dict comprehension:
your_dict = {k:v for k,v in your_dict.items() if v != 'DNC'}
From the docs on iteritems(),iterkeys() and itervalues():
Using iteritems(), iterkeys() or itervalues() while adding or
deleting entries in the dictionary may raise a RuntimeError or fail
to iterate over all entries.
Same applies to the normal for key in dict: loop.
In Python 3 this is applicable to dict.keys(), dict.values() and dict.items().
You just need to make sure that you aren't modifying the dictionary while you are iterating over it else you would get RuntimeError: dictionary changed size during iteration.
So you need to iterate over a copy of the keys, values (for d use d.items() in 2.x or list(d.items()) in 3.x)
>>> d = {'NameofEntry1': '0', 'NameofEntry2': 'DNC'}
>>> for k,v in d.items():
... if v == 'DNC':
... del d[k]
...
>>> d
{'NameofEntry1': '0'}
This should work:
for key, value in dic.items():
if value == 'DNC':
dic.pop(key)
If restrictions re: modifying the dictionary while iterating on it is a problem, you could create a new class compatible with dict that stores a reverse index of all keys that have the given value (updated at create / update / delete of dict item), which can be arguments to del without iterating over the dict's items.
Subclass dict, and override __setitem__, __delitem__, pop, popitem and clear.
If this is an operation you're doing a lot of, that might be convenient and fast.
Related
I'm tasked with creating a list of the values associated with a specific key from a list of dictionaries.
Currently, I'm two for loops and a conditional into this but I know there's a much more efficient way of going about this.
#lst = list of dict.
#k = specific key being checked for
for dict in lst:
for ind in dict:
if dict[ind] == k:
ret_val.append(dict[ind])
The instructions also state that I am to assume that all dictionaries contain the key and that the value should only be added if the value doesn't already exist.
You might be looking for this:
ret_val = [dict[ind] for dict in lst for ind in dict if dict[ind]==k]
I have a dictionary with unique values and I want to invert it (i.e. swap keys with values) inplace.
Is there any way doing it without using another dictionary?
I would prefer to just manipulate the items inside the dict than use a new dictionary, so that id(my_dict) would remain the same.
If you are trying to swap keys and values and do not mind duplicate values creating key conflicts, you can "reverse" the dictionary rather easily with a single line of code:
dictionary = dict(map(reversed, dictionary.items()))
If you would rather use the dictionary comprehension syntax instead, you can write this line:
dictionary = {value: key for key, value in dictionary.items()}
If you do not want to use the items method of the dictionary, it is rather easy to avoid:
dictionary = {dictionary[key]: key for key in dictionary}
If you can afford creating a copy of the dictionary, you can reverse it in place if needed:
def reverse_in_place(dictionary):
reference = dictionary.copy()
dictionary.clear()
dictionary.update(map(reversed, reference.items()))
I guess you want to swap the keys and the values of the dict?
You can do it like this:
dict_name = dict(zip(dict_name.values(), dict_name.keys()))
This question already has answers here:
Extract a subset of key-value pairs from dictionary?
(14 answers)
Closed 6 years ago.
I've a dictionary my_dict and a list of tokens my_tok as shown:
my_dict = {'tutor': 3,
'useful': 1,
'weather': 1,
'workshop': 3,
'thankful': 1,
'puppy': 1}
my_tok = ['workshop',
'puppy']
Is it possible to retain in my_dict, only the values present in my_tok rather than popping the rest?
i.e., I need to retain only workshop and puppy.
Thanks in advance!
Just overwrite it like so:
my_dict = {k:v for k, v in my_dict.items() if k in my_tok}
This is a dictionary comprehension that recreates my_dict using only the keys that are present as entries in the my_tok list.
As said in the comments, if the number of elemenst in the my_tok list is small compaired to the dictionary keys, this solution is not the most efficient one. In that case it would be much better to iterate through the my_tok list instead as follows:
my_dict = {k:my_dict.get(k, default=None) for k in my_tok}
which is more or less what the other answers propose. The only difference is the use of .get dictionary method with allows us not to care whether the key is present in the dictionary or not. If it isn't it would be assigned the default value.
Going over the values from the my_tok, and get the results that are within the original dictionary.
my_dict = {i:my_dict[i] for i in my_tok}
Create a new copy
You can simply overwrite the original dictionary:
new_dic = {token:my_dict[key] for key in my_tok if key in my_dict}
Mind however that you construct a new dictionary (perhaps you immediately writ it to my_dict) but this has implications: other references to the dictionary will not reflect this change.
Since the number of tokens (my_tok) are limited, it is probably better to iterate over these tokens and do a contains-check on the dictionary (instead of looping over the tuples in the original dictionary).
Update the original dictionary
Given you want to let the changes reflect in your original dictionary, you can in a second step you can .clear() the original dictionary and .update() it accordingly:
new_dic = {token:my_dict[key] for key in my_tok if key in my_dict}
my_dict.clear()
my_dict.update(new_dic)
This question already has an answer here:
deleting entries in a dictionary based on a condition
(1 answer)
Closed 8 years ago.
I'm trying to drop items from a dictionary if the value of the key is below a certain threshold. For a simple example to what I mean:
my_dict = {'blue': 1, 'red': 2, 'yellow': 3, 'green': 4}
for color in my_dict:
threshold_value = 3
if my_dict[color] < threshold_value:
del my_dict[color]
print(my_dict)
Now, I get a RuntimeError: dictionary changed size during iteration error. No big surprises there. The reason I'm posting this question is:
Find out if there's an elegant solution that doesn't require creating a new dictionary (that holds only the keys with values >= threshold).
Try to understand Python's rationale here. The way I read it to myself is: "go to the first key. Is the value of that key < x ? if yes - del this key:value item and continue on the the next key in the dictionary, if no - continue to next key without doing anything". In other words, what happened historically to previous keys shouldn't affect where I go next. I'm looking forward to the next items, regardless of the past.
I know it's a funny (some might say stupid, I'll give you that) but what's Python's "way of thinking" about this loop? Why doesn't it work? How would Python read it out loud to itself? Just trying to get a better understanding of the language...
Due to the fact that Python dictionaries are implemented as hash tables, you shouldn't rely on them having any sort of an order. Key order may change unpredictably (but only after insertion or removal of a key). Thus, it's impossible to predict the next key. Python throws the RuntimeError to be safe, and to prevent people from running into unexpected results.
Python 2's dict.items method returns a copy of key-value pairs, so you can safely iterate over it and delete values you don't need by keys, as #wim suggested in comments. Example:
for k, v in my_dict.items():
if v < threshold_value:
del my_dict[k]
However, Python 3's dict.items returns a view object that reflects all changes made to the dictionary. This is the reason the solution above only works in Python 2. You may convert my_dict.items() to list (tuple etc.) to make it Python 3-compatible.
Another way to approach the problem is to select keys you want to delete and then delete them
keys = [k for k, v in my_dict.items() if v < threshold_value]
for x in keys:
del my_dict[x]
This works in both Python 2 and Python 3.
Dictionaries are unordered. By deleting one key nobody can say, what the next key is. So python in general disallow to add or remove keys from a dictionary, over that is iterated.
Just create a new one:
my_dict = {"blue":1,"red":2,"yellow":3,"green":4}
new_dict = {k:v for k,v in my_dict.iteritems() if v >= threshold_value}
I guess that modifying a collection while iterating over it is a hard thing to do to implement properly. Consider following exaple:
>>> list = [1, 2, 3, 4, 5, 6]
>>> for ii in range(len(list)):
print list[ii];
if list[ii] == 3:
del list[ii]
1
2
3
5
6
Notice that in this example 4 was altogether omitted. It is very similat in dictionaries, deleting/adding entries might invalidate internal structures that define order of iteration (for example you deleted enough entries so hash map bucket size changed).
To solve your case --- just create new dictionary and copy items there. As to
I have a dictionary dict1; each value is a list of strings. If all elements in this list of strings contain 'my_string', I don't need this particular key. I've come up with this:
from collections import defaultdict
dict2 = defaultdict(list)
for key, value in dict1.iteritems():
for list_element in value:
if 'my_string' not in list_element:
dict2[key] = dict1[key]
It does work but I'm sure there is a better way of doing it. (And I'd prefer not to create another dictionary, which happens in the code above, but it's not really important.)
You can't modify a dict while iterating over it. You either need to create a new dict by filtering the old, or create a temporary object of some kind to iterate over:
(1) Create a new dict with the filtered results:
dict1 = {k:v for (k, v) in dict1.iteritems() if all('my_string' in e for e in v)}
(2.1) Create a temporary dict:
for k, v in dict1.copy():
if all('my_string' in e for e in v):
del dict1[k]
(2.2) Create a temporary list of key-value tuples:
for k, v in dict1.items():
if all('my_string' in e for e in v):
del dict1[k]
(2.3) Create a temporary list of keys:
for k in dict1.keys():
if all('my_string' in e for e in dict1[k]):
del dict1[k]
So, how do you decide between them?
Well, 1 is easiest to reason about, because it has all of the benefits of mutation-free code. But 2.1-2.3 are probably more straightforward for a novice programmer. Usually, that distinction is the most important one.
If you're worried about memory usage, obviously 2.3 is better than 2.1-2.2, because it generates a much smaller temporary object. But what about 2.3 vs. 1? That depends on two things: First, how big is a list of all of your keys compared to a dict of just your remaining items? Second, how much space is gained by building a smaller hash table from scratch instead of shrinking a larger one? Usually, you don't get any benefit from the latter, because Python doesn't shrink the hash table at all… but if it matters, you need to test with your use cases on your platform, and see what happens.
If you're worried about performance, it's pretty similar to memory usage. 2.3 vs. 1 are the obvious contenders, and 1 will be better unless you're keeping most of the dict around—but again, if it matters, you need to measure for yourself.
Finally, note that the above is for Python 2.7, which is what (as a guess) you seem to be using. In 3.x, items and keys both return iterators over the existing dict, so you need to do list(dict1.items()) and list(dict1.keys()) to make the copying explicit.
for key, value in dict1.items():
if all('my_string' in e for e in value):
del dict1[key]
Note: be careful not to use iteritems and delete from the same dict. items are fine, it makes a copy.
I think you can just use a dictionary comprehension, if that's available in your version:
filtered = {k:v for k,v in d1.items() if all(e == 'my_string' for e in v)}
This assumes you don't mind making a second dictionary that is a filtered copy of the first.