How to iterate over a dynamic object? - python

I have some code that iterates over the values of a dictionary. If the value meets certain conditions, it is deleted from the dictionary. Those conditions are contingent on the existence of other values in the dictionary. This is why I don't want to just copy the old dictionary and make deletions, then re-attribute it.
When I try to run it, I get an error that the size of the dictionary changed while iterating it.
Is there a way to iterate over a dictionary that allows it to change size, and the existence of keys and values, while it is iterating?

Build a new dictionary which contains the keys you want to keep. This can be done with a dictionary comprehension, or a manual for loop.
Here's a comprehension:
return {k: v for k, v in my_dict.items() if some-condition}
Here's a manual loop:
result = {}
for k, v in my_dict.items():
if some-condition:
result[k] = v
return result

Well, yes you can iterate on by the keys (Python3)! Take a look:
>>> dc
{1: 'aze', 3: 'poi', 4: 'mlk'}
>>> dc = {1:"aze", 2:"qsd", 3:"poi", 4:"mlk"}
>>> dc
{1: 'aze', 2: 'qsd', 3: 'poi', 4: 'mlk'}
>>> keys = list(dc.keys())
>>> keys
[1, 2, 3, 4]
>>> for k in keys:
if "q" in dc[k]:
del dc[k]
>>> dc
{1: 'aze', 3: 'poi', 4: 'mlk'}
>>>

You can iterate over the keys instead of over the dict itself. In the following example, all values that are odd-numbered are removed from the dict:
>>> a = {'a': 12, 'b': 3, 'c': 14}
>>> for key in list(a.keys()):
if a[key] % 2 == 0:
del a[key]
>>> a
{'b': 3}

Related

How to print a dictionary as key and count (if values is list) without for loop?

Suppose I have a dictionary as follows
dic = {0: [1,2,3,4,5], 1:[7,4,6]}
While printing the dictionary as key and count, first updating the dictionary
for k, v in dic.items():
dic[k] = len(v)
print(dic)
>>> {0:5, 1:3}
Is there a better way to do the above without for loop?
What do you mean saying without iteration? If you don't want to use a for loop, you can use a map function:
d = dict(map(lambda kv: (kv[0], len(kv[1])), d.items()))
If by no iteration you simply mean to do it without a for loop, you can map the dict values to the len function, zip them with the dict keys and pass the zipped key-value pairs to the dict constructor:
>>> d = {0: [1,2,3,4,5], 1:[7,4,6]}
>>> dict(zip(d, map(len, d.values())))
{0: 5, 1: 3}
>>>
First of all do not name a list 'list' or a dictionary 'dict', as this is a reserved word in python for the class that holds that data type.
You can do it neatly using dictionary comprehension as follows:
d = {0: [1,2,3,4,5], 1:[7,4,6]}
d = {i:len(j) for i,j in d.items()}
print(d)
Or use:
print({k:len(dic[k]) for k in dic})
Output:
{0: 5, 1: 3}

Slicing a dictionary

I have a dictionary, and would like to pass a part of it to a function, that part being given by a list (or tuple) of keys. Like so:
# the dictionary
d = {1:2, 3:4, 5:6, 7:8}
# the subset of keys I'm interested in
l = (1,5)
Now, ideally I'd like to be able to do this:
>>> d[l]
{1:2, 5:6}
... but that's not working, since it will look for a key matching the tuple (1,5), the same as d[1,5].
d{1,5} isn't even valid Python (as far as I can tell ...), though it might be handy: The curly braces suggest an unordered set or a dictionary, so returning a dictionary containing the specified keys would look very plausible to me.
d[{1,5}] would also make sense ("here's a set of keys, give me the matching items"), and {1, 5} is an unhashable set, so there can't be a key that matches it -- but of course it throws an error, too.
I know I can do this:
>>> dict([(key, value) for key,value in d.iteritems() if key in l])
{1: 2, 5: 6}
or this:
>>> dict([(key, d[key]) for key in l])
which is more compact
... but I feel there must be a "better" way of doing this. Am I missing a more elegant solution?
(I'm using Python 2.7)
On Python 3 you can use the itertools islice to slice the dict.items() iterator
import itertools
d = {1: 2, 3: 4, 5: 6}
dict(itertools.islice(d.items(), 2))
{1: 2, 3: 4}
Note: this solution does not take into account specific keys. It slices by internal ordering of d, which in Python 3.7+ is guaranteed to be insertion-ordered.
You should be iterating over the tuple and checking if the key is in the dict not the other way around, if you don't check if the key exists and it is not in the dict you are going to get a key error:
print({k:d[k] for k in l if k in d})
Some timings:
{k:d[k] for k in set(d).intersection(l)}
In [22]: %%timeit
l = xrange(100000)
{k:d[k] for k in l}
....:
100 loops, best of 3: 11.5 ms per loop
In [23]: %%timeit
l = xrange(100000)
{k:d[k] for k in set(d).intersection(l)}
....:
10 loops, best of 3: 20.4 ms per loop
In [24]: %%timeit
l = xrange(100000)
l = set(l)
{key: d[key] for key in d.viewkeys() & l}
....:
10 loops, best of 3: 24.7 ms per
In [25]: %%timeit
l = xrange(100000)
{k:d[k] for k in l if k in d}
....:
100 loops, best of 3: 17.9 ms per loop
I don't see how {k:d[k] for k in l} is not readable or elegant and if all elements are in d then it is pretty efficient.
To slice a dictionary, Convert it to a list of tuples using d.items(), slice the list and create a dictionary out of it.
Here.
d = {1:2, 3:4, 5:6, 7:8}
To get the first 2 items
first_two = dict(list(d.items())[:2])
first_two
{1: 2, 3: 4}
Use a set to intersect on the dict.viewkeys() dictionary view:
l = {1, 5}
{key: d[key] for key in d.viewkeys() & l}
This is Python 2 syntax, in Python 3 use d.keys().
This still uses a loop, but at least the dictionary comprehension is a lot more readable. Using set intersections is very efficient, even if d or l is large.
Demo:
>>> d = {1:2, 3:4, 5:6, 7:8}
>>> l = {1, 5}
>>> {key: d[key] for key in d.viewkeys() & l}
{1: 2, 5: 6}
Write a dict subclass that accepts a list of keys as an "item" and returns a "slice" of the dictionary:
class SliceableDict(dict):
default = None
def __getitem__(self, key):
if isinstance(key, list): # use one return statement below
# uses default value if a key does not exist
return {k: self.get(k, self.default) for k in key}
# raises KeyError if a key does not exist
return {k: self[k] for k in key}
# omits key if it does not exist
return {k: self[k] for k in key if k in self}
return dict.get(self, key)
Usage:
d = SliceableDict({1:2, 3:4, 5:6, 7:8})
d[[1, 5]] # {1: 2, 5: 6}
Or if you want to use a separate method for this type of access, you can use * to accept any number of arguments:
class SliceableDict(dict):
def slice(self, *keys):
return {k: self[k] for k in keys}
# or one of the others from the first example
d = SliceableDict({1:2, 3:4, 5:6, 7:8})
d.slice(1, 5) # {1: 2, 5: 6}
keys = 1, 5
d.slice(*keys) # same
set intersection and dict comprehension can be used here
# the dictionary
d = {1:2, 3:4, 5:6, 7:8}
# the subset of keys I'm interested in
l = (1,5)
>>>{key:d[key] for key in set(l) & set(d)}
{1: 2, 5: 6}
the dictionary
d = {1:2, 3:4, 5:6, 7:8}
the subset of keys I'm interested in
l = (1,5)
answer
{key: d[key] for key in l}
Another option is to convert the dictionary into a pandas Series object and then locating the specified indexes:
>>> d = {1:2, 3:4, 5:6, 7:8}
>>> l = [1,5]
>>> import pandas as pd
>>> pd.Series(d).loc[l].to_dict()
{1: 2, 5: 6}
My case is probably relatively uncommon, but, I'm posting it here nonetheless in case it helps someone (though not OP directly).
I came across this question searching how to slice a dictionary that had item counts. Basically I had a dictionary where the keys were letters, and the values were the number of times the letter appeared (i.e. abababc --> {'a': 3, 'b': 3, 'c': 1} I wanted to 'slice' the dictionary so that I could return the most common n keys.
It turns out that this is exactly what a Collections Counter object is for, and instead of needing to 'slice' my dictionary, I could easily just convert it to a collections.Counter and then call most_common(n): https://docs.python.org/3/library/collections.html#collections.Counter.most_common
You can do slicing of the dictionary with the help of the module dictionarify
This is the link for documentation-https://github.com/suryavenom/Flexi/blob/main/README.md.
Installation -
pip install dictionarify

Python: Dictionary changed size during iteration" [duplicate]

I have a dictionary of lists in which some of the values are empty:
d = {'a': [1], 'b': [1, 2], 'c': [], 'd':[]}
At the end of creating these lists, I want to remove these empty lists before returning my dictionary. I tried doing it like this:
for i in d:
if not d[i]:
d.pop(i)
but I got a RuntimeError. I am aware that you cannot add/remove elements in a dictionary while iterating through it...what would be a way around this then?
See Modifying a Python dict while iterating over it for citations that this can cause problems, and why.
In Python 3.x and 2.x you can use use list to force a copy of the keys to be made:
for i in list(d):
In Python 2.x calling keys made a copy of the keys that you could iterate over while modifying the dict:
for i in d.keys():
But note that in Python 3.x this second method doesn't help with your error because keys returns an a view object instead of copying the keys into a list.
You only need to use copy:
This way you iterate over the original dictionary fields and on the fly can change the desired dict d.
It works on each Python version, so it's more clear.
In [1]: d = {'a': [1], 'b': [1, 2], 'c': [], 'd':[]}
In [2]: for i in d.copy():
...: if not d[i]:
...: d.pop(i)
...:
In [3]: d
Out[3]: {'a': [1], 'b': [1, 2]}
(BTW - Generally to iterate over copy of your data structure, instead of using .copy for dictionaries or slicing [:] for lists, you can use import copy -> copy.copy (for shallow copy which is equivalent to copy that is supported by dictionaries or slicing [:] that is supported by lists) or copy.deepcopy on your data structure.)
Just use dictionary comprehension to copy the relevant items into a new dict:
>>> d
{'a': [1], 'c': [], 'b': [1, 2], 'd': []}
>>> d = {k: v for k, v in d.items() if v}
>>> d
{'a': [1], 'b': [1, 2]}
For this in Python 2:
>>> d
{'a': [1], 'c': [], 'b': [1, 2], 'd': []}
>>> d = {k: v for k, v in d.iteritems() if v}
>>> d
{'a': [1], 'b': [1, 2]}
This worked for me:
d = {1: 'a', 2: '', 3: 'b', 4: '', 5: '', 6: 'c'}
for key, value in list(d.items()):
if value == '':
del d[key]
print(d)
# {1: 'a', 3: 'b', 6: 'c'}
Casting the dictionary items to list creates a list of its items, so you can iterate over it and avoid the RuntimeError.
I would try to avoid inserting empty lists in the first place, but, would generally use:
d = {k: v for k,v in d.iteritems() if v} # re-bind to non-empty
If prior to 2.7:
d = dict( (k, v) for k,v in d.iteritems() if v )
or just:
empty_key_vals = list(k for k in k,v in d.iteritems() if v)
for k in empty_key_vals:
del[k]
To avoid "dictionary changed size during iteration error".
For example: "when you try to delete some key",
Just use 'list' with '.items()'. Here is a simple example:
my_dict = {
'k1':1,
'k2':2,
'k3':3,
'k4':4
}
print(my_dict)
for key, val in list(my_dict.items()):
if val == 2 or val == 4:
my_dict.pop(key)
print(my_dict)
Output:
{'k1': 1, 'k2': 2, 'k3': 3, 'k4': 4}
{'k1': 1, 'k3': 3}
This is just an example. Change it based on your case/requirements.
For Python 3:
{k:v for k,v in d.items() if v}
You cannot iterate through a dictionary while itโ€™s changing during a for loop. Make a casting to list and iterate over that list. It works for me.
for key in list(d):
if not d[key]:
d.pop(key)
Python 3 does not allow deletion while iterating (using the for loop above) a dictionary. There are various alternatives to do it; one simple way is to change the line
for i in x.keys():
with
for i in list(x)
The reason for the runtime error is that you cannot iterate through a data structure while its structure is changing during iteration.
One way to achieve what you are looking for is to use a list to append the keys you want to remove and then use the pop function on dictionary to remove the identified key while iterating through the list.
d = {'a': [1], 'b': [1, 2], 'c': [], 'd':[]}
pop_list = []
for i in d:
if not d[i]:
pop_list.append(i)
for x in pop_list:
d.pop(x)
print (d)
For situations like this, I like to make a deep copy and loop through that copy while modifying the original dict.
If the lookup field is within a list, you can enumerate in the for loop of the list and then specify the position as the index to access the field in the original dict.
Nested null values
Let's say we have a dictionary with nested keys, some of which are null values:
dicti = {
"k0_l0":{
"k0_l1": {
"k0_l2": {
"k0_0":None,
"k1_1":1,
"k2_2":2.2
}
},
"k1_l1":None,
"k2_l1":"not none",
"k3_l1":[]
},
"k1_l0":"l0"
}
Then we can remove the null values using this function:
def pop_nested_nulls(dicti):
for k in list(dicti):
if isinstance(dicti[k], dict):
dicti[k] = pop_nested_nulls(dicti[k])
elif not dicti[k]:
dicti.pop(k)
return dicti
Output for pop_nested_nulls(dicti)
{'k0_l0': {'k0_l1': {'k0_l2': {'k1_1': 1,
'k2_2': 2.2}},
'k2_l1': 'not '
'none'},
'k1_l0': 'l0'}
The Python "RuntimeError: dictionary changed size during iteration" occurs when we change the size of a dictionary when iterating over it.
To solve the error, use the copy() method to create a shallow copy of the dictionary that you can iterate over, e.g., my_dict.copy().
my_dict = {'a': 1, 'b': 2, 'c': 3}
for key in my_dict.copy():
print(key)
if key == 'b':
del my_dict[key]
print(my_dict) # ๐Ÿ‘‰๏ธ {'a': 1, 'c': 3}
You can also convert the keys of the dictionary to a list and iterate over the list of keys.
my_dict = {'a': 1, 'b': 2, 'c': 3}
for key in list(my_dict.keys()):
print(key)
if key == 'b':
del my_dict[key]
print(my_dict) # ๐Ÿ‘‰๏ธ {'a': 1, 'c': 3}
If the values in the dictionary were unique too, then I used this solution:
keyToBeDeleted = None
for k, v in mydict.items():
if(v == match):
keyToBeDeleted = k
break
mydict.pop(keyToBeDeleted, None)

Dictionary comprehension for swapping keys/values in a dict with multiple equal values

def invert_dict(d):
inv = dict()
for key in d:
val = d[key]
if val not in inv:
inv[val] = [key]
else:
inv[val].append(key)
return inv
This is an example from Think Python book, a function for inverting(swapping) keys and values in a dictionary. New values (former keys) are stored as lists, so if there was multiple dictionary values (bound to a different keys) that were equal before inverting, then this function simply appends them to the list of former keys.
Example:
somedict = {'one': 1, 'two': 2, 'doubletwo': 2, 'three': 3}
invert_dict(somedict) ---> {1: ['one'], 2: ['doubletwo', 'two'], 3: ['three']}
My question is, can the same be done with dictionary comprehensions? This function creates an empty dict inv = dict(), which is then checked later in the function with if/else for the presence of values. Dict comprehension, in this case, should check itself. Is that possible, and how the syntax should look like?
General dict comprehension syntax for swapping values is:
{value:key for key, value in somedict.items()}
but if I want to add an 'if' clausule, what it should look like? if value not in (what)?
Thanks.
I don't think it's possible with simple dict comprehension without using other functions.
Following code uses itertools.groupby to group keys that have same values.
>>> import itertools
>>> {k: [x[1] for x in grp]
for k, grp in itertools.groupby(
sorted((v,k) for k, v in somedict.iteritems()),
key=lambda x: x[0])
}
{1: ['one'], 2: ['doubletwo', 'two'], 3: ['three']}
You can use a set comprehension side effect:
somedict = {'one': 1, 'two': 2, 'doubletwo': 2, 'three': 3}
invert_dict={}
{invert_dict.setdefault(v, []).append(k) for k, v in somedict.items()}
print invert_dict
# {1: ['one'], 2: ['doubletwo', 'two'], 3: ['three']}
Here is a good answer:
fts = {1:1,2:1,3:2,4:1}
new_dict = {dest: [k for k, v in fts.items() if v == dest] for dest in set(fts.values())}
Reference: Head First Python ,2nd Edition, Page(502)

How do I exchange keys with values in a dictionary? [duplicate]

This question already has answers here:
Reverse / invert a dictionary mapping
(32 answers)
Closed 10 months ago.
I receive a dictionary as input, and would like to to return a dictionary whose keys will be the input's values and whose value will be the corresponding input keys. Values are unique.
For example, say my input is:
a = dict()
a['one']=1
a['two']=2
I would like my output to be:
{1: 'one', 2: 'two'}
To clarify I would like my result to be the equivalent of the following:
res = dict()
res[1] = 'one'
res[2] = 'two'
Any neat Pythonic way to achieve this?
Python 2:
res = dict((v,k) for k,v in a.iteritems())
Python 3 (thanks to #erik):
res = dict((v,k) for k,v in a.items())
new_dict = dict(zip(my_dict.values(), my_dict.keys()))
From Python 2.7 on, including 3.0+, there's an arguably shorter, more readable version:
>>> my_dict = {'x':1, 'y':2, 'z':3}
>>> {v: k for k, v in my_dict.items()}
{1: 'x', 2: 'y', 3: 'z'}
You can make use of dict comprehensions:
Python 3
res = {v: k for k, v in a.items()}
Python 2
res = {v: k for k, v in a.iteritems()}
Edited: For Python 3, use a.items() instead of a.iteritems(). Discussions about the differences between them can be found in iteritems in Python on SO.
In [1]: my_dict = {'x':1, 'y':2, 'z':3}
Python 3
In [2]: dict((value, key) for key, value in my_dict.items())
Out[2]: {1: 'x', 2: 'y', 3: 'z'}
Python 2
In [2]: dict((value, key) for key, value in my_dict.iteritems())
Out[2]: {1: 'x', 2: 'y', 3: 'z'}
The current leading answer assumes values are unique which is not always the case. What if values are not unique? You will loose information!
For example:
d = {'a':3, 'b': 2, 'c': 2}
{v:k for k,v in d.iteritems()}
returns {2: 'b', 3: 'a'}.
The information about 'c' was completely ignored.
Ideally it should had be something like {2: ['b','c'], 3: ['a']}. This is what the bottom implementation does.
Python 2.x
def reverse_non_unique_mapping(d):
dinv = {}
for k, v in d.iteritems():
if v in dinv:
dinv[v].append(k)
else:
dinv[v] = [k]
return dinv
Python 3.x
def reverse_non_unique_mapping(d):
dinv = {}
for k, v in d.items():
if v in dinv:
dinv[v].append(k)
else:
dinv[v] = [k]
return dinv
You could try:
Python 3
d={'one':1,'two':2}
d2=dict((value,key) for key,value in d.items())
d2
{'two': 2, 'one': 1}
Python 2
d={'one':1,'two':2}
d2=dict((value,key) for key,value in d.iteritems())
d2
{'two': 2, 'one': 1}
Beware that you cannot 'reverse' a dictionary if
More than one key shares the same value. For example {'one':1,'two':1}. The new dictionary can only have one item with key 1.
One or more of the values is unhashable. For example {'one':[1]}. [1] is a valid value but not a valid key.
See this thread on the python mailing list for a discussion on the subject.
res = dict(zip(a.values(), a.keys()))
new_dict = dict( (my_dict[k], k) for k in my_dict)
or even better, but only works in Python 3:
new_dict = { my_dict[k]: k for k in my_dict}
Another way to expand on Ilya Prokin's response is to actually use the reversed function.
dict(map(reversed, my_dict.items()))
In essence, your dictionary is iterated through (using .items()) where each item is a key/value pair, and those items are swapped with the reversed function. When this is passed to the dict constructor, it turns them into value/key pairs which is what you want.
Suggestion for an improvement for Javier answer :
dict(zip(d.values(),d))
Instead of d.keys() you can write just d, because if you go through dictionary with an iterator, it will return the keys of the relevant dictionary.
Ex. for this behavior :
d = {'a':1,'b':2}
for k in d:
k
'a'
'b'
Can be done easily with dictionary comprehension:
{d[i]:i for i in d}
dict(map(lambda x: x[::-1], YourDict.items()))
.items() returns a list of tuples of (key, value). map() goes through elements of the list and applies lambda x:[::-1] to each its element (tuple) to reverse it, so each tuple becomes (value, key) in the new list spitted out of map. Finally, dict() makes a dict from the new list.
Hanan's answer is the correct one as it covers more general case (the other answers are kind of misleading for someone unaware of the duplicate situation). An improvement to Hanan's answer is using setdefault:
mydict = {1:a, 2:a, 3:b}
result = {}
for i in mydict:
result.setdefault(mydict[i],[]).append(i)
print(result)
>>> result = {a:[1,2], b:[3]}
Using loop:-
newdict = {} #Will contain reversed key:value pairs.
for key, value in zip(my_dict.keys(), my_dict.values()):
# Operations on key/value can also be performed.
newdict[value] = key
If you're using Python3, it's slightly different:
res = dict((v,k) for k,v in a.items())
Adding an in-place solution:
>>> d = {1: 'one', 2: 'two', 3: 'three', 4: 'four'}
>>> for k in list(d.keys()):
... d[d.pop(k)] = k
...
>>> d
{'two': 2, 'one': 1, 'four': 4, 'three': 3}
In Python3, it is critical that you use list(d.keys()) because dict.keys returns a view of the keys. If you are using Python2, d.keys() is enough.
I find this version the most comprehensive one:
a = {1: 'one', 2: 'two'}
swapped_a = {value : key for key, value in a.items()}
print(swapped_a)
output :
{'one': 1, 'two': 2}
An alternative that is not quite as readable (in my opinion) as some of the other answers:
new_dict = dict(zip(*list(zip(*old_dict.items()))[::-1]))
where list(zip(*old_dict.items()))[::-1] gives a list of 2 tuples, old_dict's values and keys, respectively.

Categories