How to solve dictionary changed size during iteration error? - python

I want pop out all the large values and its keys in a dictionary, and keep the smallest. Here is the part of my program
for key,value in dictionary.items():
for key1, value1 in dictionary.items():
if key1!= key and value > value1:
dictionary.pop(key)
print (dictionary)
Which results in
RuntimeError: dictionary changed size during iteration
How can I avoid this error?

In Python3, Try
for key in list(dict.keys()):
if condition:
matched
del dict[key]
1 more thing should be careful when looping a dict to update its key:
Code1:
keyPrefix = ‘keyA’
for key, value in Dict.items():
newkey = ‘/’.join([keyPrefix, key])
Dict[newkey] = Dict.pop(key)
Code2:
keyPrefix = ‘keyA’
for key, value in Dict.keys():
newkey = ‘/’.join([keyPrefix, key])
Dict[newkey] = Dict.pop(key)
Result of code1/code2 is:
{‘keyA/keyA/keyB’ : ”, ‘keyA/keyA/keyA’: ”}
My way to resolve this unexpected result:
Dict = {‘/’.join([keyPrefix, key]): value for key, value in Dict.items()}
Link: https://blog.gainskills.top/2016/07/21/loop-a-dict-to-update-key/

Alternative solutions
If you're looking for the smallest value in the dictionary you can do this:
min(dictionary.values())
If you cannot use min, you can use sorted:
sorted(dictionary.values())[0]
Why do I get this error?
On a side note, the reason you're experiencing an Runtime Error is that in the inner loop you modify the iterator your outer loop is based upon. When you pop an entry that is yet to be reached by the outer loop and the outer iterator reaches it, it tries to access a removed element, thus causing the error.
If you try to execute your code on Python 2.7 (instead of 3.x) you'll get, in fact, a Key Error.
What can I do to avoid the error?
If you want to modify an iterable inside a loop based on its iterator you should use a deep copy of it.

You can use copy.deepcopy to make a copy of the original dict, loop over the copy while change the original one.
from copy import deepcopy
d=dict()
for i in range(5):
d[i]=str(i)
k=deepcopy(d)
d[2]="22"
print(k[2])
#The result will be 2.
Your problem is iterate over something that you are changing.

Record the key during the loop and then do dictionary.pop(key) when loop is done. Like this:
for key,value in dictionary.items():
for key1, value1 in dictionary.items():
if key1!= key and value > value1:
storedvalue = key
dictionary.pop(key)

Here is one way to solve it:
From the dictionary, get a list of keys, sorted by value
Since the first key in this list has the smallest value, you can do what you want with it.
Here is a sample:
# A list of grades, not in any order
grades = dict(John=95,Amanda=89,Jake=91,Betty=97)
# students is a list of students, sorted from lowest to highest grade
students = sorted(grades, key=lambda k: grades[k])
print 'Grades from lowest to highest:'
for student in students:
print '{0} {1}'.format(grades[student], student)
lowest_student = students[0]
highest_student = students[-1]
print 'Lowest grade of {0} belongs to {1}'.format(grades[lowest_student], lowest_student)
print 'Highest grade of {0} belongs to {1}'.format(grades[highest_student], highest_student)
The secret sauce here is in the sorted() function: instead of sorting by keys, we sorted by values.

If you want to just keep the key with the smallest value, I would do it by first finding that item and then creating a new dictionary containing only it. If your dictionary was d, something like this would do that in one line:
d = dict((min(d.items(), key=lambda item: item[1]),))
This will not only avoid any issues about updating the dictionary while iterating it, it is probably faster than removing all the other elements.
If you must do the modifications in-place for some reason, the following would work because it makes a copy of all the keys before modifying the dictionary:
key_to_keep = min(d.items(), key=lambda item: item[1])[0]
for key in list(d):
if key != key_to_keep:
d.pop(key)

As I read your loop right now, you're looking to keep only the single smallest element, but without using min. So do the opposite of what your code does now, check if value1 < minValueSoFar, if so, keep key1 as minKeySoFar. Then at the end of the loop (as Zayatzz suggested), do a dictionary.pop(minKeySoFar)
As an aside, I note that the key1!=key test is irrelevant and computationally inefficient assuming a reasonably long list.
minValueSoFar = 9999999; # or whatever
for key,value in dictionary.items():
if value < minValueSoFar:
minValueSoFar = value
minKeySoFar = key
dictionary.pop(minKeySoFar) # or whatever else you want to do with the result

An alternative solution to dictionary changed size during iteration:
for key,value in list(dictionary.items()):
for key1, value1 in list(dictionary.items()):
if key1!= key and value > value1:
dictionary.pop(key)
print (dictionary)
Better use it with caution! when using this type of code, because list(dictionary.items()) calculated when the complier enters first time to loop. Therefore any change made on dictionary won't affect process inside the current loop.

You could create a list with the vaules you want to delete and than run a second for loop:
for entry in (listofkeystopop):
dictionary.pop(entry)

Related

List of tuples of arbitrary length to nested dictionary

So I have a list of tuples of the form:
my_list=[('a','aa','aaa',0),('a','aa','aab',1),('a','ab','aba',2)]
And I need it to convert it to a nested dictionary:
out={'a':{'aa':{'aaa':0,'aab':1},'ab':{'aba':2}}}
The crucial piece of my setting is that I do not know in advance the length of the tuples in my_list (4 is just an example). Is there an easy way to generalize the cases I saw in other answers (e.g. Python list of tuples to nested dict or Convert a list of variable length Tuples into Dictionary) beyond the fixed 3-element tuples they use?
I made various attempts with recursive functions but I do not have anything close to a solution.
Just loop over the first keys, setting aside the last pair of items which will be a key-value pair, and set dictionaries in-between (using setdefault here, but you could do that part manually):
result = {}
for *keys, last_key, value in my_list:
current = result
for key in keys:
current = current.setdefault(key, {})
current[last_key] = value
Just to be explicit by what I mean by "manually" (I guess I should rather say "explicitly"):
result = {}
for *keys, last_key, value in my_list:
current = result
for key in keys:
if key not in current:
current[key] = {}
current = current[key]
current[last_key] = value

access the value of the key outside of for loop when key's value in the loop equals the key Python

I have two list of dictionaries:
sftoap = [{'0060z000023GQYKAA4': 'ID-2522'},
{'0060z000023GQZNAA4': 'ID-2523'},
{'0060z000023GQidAAG': 'ID-2524'}]
opp_products = [{'Opportunity_ID__c': '0060z000023GQYKAA4', 'TotalPrice': 50408.22991509},
{'Opportunity_ID__c': '0060z000023GQZNAA4', 'TotalPrice': 50408.22991509},
{'Opportunity_ID__c': '0060z000023GQighTY', 'TotalPrice': 50408.22991509}]
I need to loop through opp_products and assign a key's value from sftoap list of dictionaries to a new variable. BUT Only if the value of 'Opportunity_ID__c' equals to the key from sftoap
Here is what I have tried:
for index, opp_products_dict in enumerate(opp_products):
for opp_products_key, opp_products_value in opp_products_dict.items():
id = opp_products_dict["Opportunity_ID__c"]
ap_opp_id = sftoap[id]
Gives me an error, because I clearly need to loop through the sftoap But don't want to have nested loops. My desired output would be:
print(ap_opp_id)
ID-2522
ID-2524
If you want to avoid a nested loop, you could create a dictionary from sftoap before you enter the loop, like this:
sftoap_dict = {}
for d in sftoap:
key, value = d.popitem()
sftoap_dict[key] = value
This way you can use ap_opp_id = sftoap_dict[id] in your original loop.
You can reorganize your sftoap list of dictionaries to a dictionary to avoid nested loop.
for example:
sftoap_dict = {}
for dicts in sftoap:
for key,val in dicts.items():
sftoap_dict[key]=val
Next, you could include exception in the second part of your code
for index, opp_products_dict in enumerate(opp_products):
for opp_products_key, opp_products_value in opp_products_dict.items():
idx = opp_products_dict["Opportunity_ID__c"]
try:
ap_opp_id = sftoap_dict[idx]
print(ap_opp_id)
except KeyError:
pass

Cannot convert a dictionary into list of tuples and sort it

I want to find outliers keys (names) in my dataset, so that I am trying to create a list of tuples from my dictionary and sort them by salary. That is my code
for key, value in data_dict.iteritems():
sorted_dict_list = []
sorted_dict_list.append((key, value["salary"], value["bonus"]))
sorted_dict_list.sort(key=lambda tup: tup[1])
for tuple in sorted_dict_list:
print tuple
The problem is that the printed output is unsorted. What might be the problem?
You should declare sorted_dict_list outside the first for loop, otherwise each time you go through the loop you have a new empty list. On top of that, your loop through sorted_dict_list is inside the first for loop.
So each time you loop through the outer loop you create an empty list, add the next key-value pair into it, run through the list (which is only one item long) and print out the values. Basically you are just printing out each key-value pair as you go through.
You need to move the list declaration outside the loop and un-indent the second loop.
sorted_dict_list = []
for key, value in data_dict.iteritems():
sorted_dict_list.append((key, value["salary"], value["bonus"]))
sorted_dict_list.sort(key=lambda tup: tup[1])
for tuple in sorted_dict_list:
print tuple
This might be a better solution:
def sortKey(item):
return item[1]
sorted_dict = [(key, value['salary'], value['bonus']) for key, value in data_dict.items()]
sorted_dict = sorted(sorted_dict, key=sortKey)
for item in sorted_dict:
print(item)

what is the meaning of this line

Can someone explain to me what this line of code means, keep in mind im new to python.
I have to print the name of the best student and his grade.
d is a composed of names of students and their respective means(grades mean)
I know this code works but i can't understand it.
b_p = d.keys()[d.values().index(max(d.values()))]
print '%s %.2f' % (b_p,d[b_p])
I tried sorting d and print d.keys()[-1] but
AttributeError: 'list' object has no attribute 'keys'
. I also have to print his grade next to the name.
Decipher it step by step, from the inside outwards:
d.values()
gets you a list (in Python 2) of values of your dictionary. That is, the grades themselves.
The highest value is then obtained:
max(d.values())
Then, the index (position) is found in the list of values for that highest value:
d.values().index(max(d.values()))
That index applies to the list of values, but also to the list of keys. So you can use that index to find the key (student name) related to the highest value, and index that list (d.keys()) with it:
d.keys()[d.values().index(max(d.values()))]
And thus, the result is the name of the person with the highest grade.
A clearer variant is to split it up into multiple lines:
grades = d.values()
maxgrade = max(grades)
maxindex = grades.index(grades)
names = d.keys()
maxname = names[maxindex]
Why your attempt failed:
I tried sorting d and print d.keys()[-1] but [got an error message:]
AttributeError: 'list' object has no attribute 'keys'
When you sort a dictionary, the result is a sorted list of just the keys: you lose the dictionary information. So after d = sorted(d), d is now a list, and does not have a method, or more generally, attribute, keys(). Hence the error message.
As to how to do this properly, refer to this question. In particular, the clearest answer there is
max(d, key=d.get)
The max function can take a key argument, and using d.get will actually use whatever d.get returns (which are the values, not the keys) to find the maximum.
You could use the same with sorted:
sorted(d, key=d.get)
will list the names, ordered from lowest to highest grade.
Notice that sorted(d) is a shorthand for sorted(d.keys()), not sorted(d.values()) or sorted(d.items())
To sort the key-value pairs by value, try:
from operator import itemgetter
best_student = sorted(d.items(), key=itemgetter(1), reverse=True)[0]

How to access dictionary elements in a list in python

I'm trying to understand about dictionaries and list in Python.
Assume that I have a list which contains 3 dictionaries
Ex. item_list = [price,stock,quantity]
price, stock and quantity are the dictionaries here. Each dictionary has few key-value pairs like
price = {'toys':25,'clothes':[12,15,20]}
I know to access individual elements, I can use a for loop with the keys specified like:
for item in item_list:
print item['toys']
How do I access the elements without directly mentioning the key in the for loop?
You can iterate over a dictionary just like you can iterate over a list.
for item in item_list:
for key in item:
print item[key]
In a sense, you're still mentioning the keys, but you're not referencing them explicitly by their value.
If you're just interested in the values you can use the values() method:
for item in item_list:
for key in item.values():
print item[key]
My preferred way to iterate over dictionaries though is with items, which returns a tuple of key, value pairs.
for item in item_list:
for key, value in item.items():
print key, value
I would do something like this:
for item in item_list:
for key, value in item.iteritems():
print key, value
Let us say, you have a list like this
prices_list = [{'toys':25,'clothes':[12,15,20]},{'toys':35,'clothes':[12,15,20]}]
And you would like to get the value corresponding to toys, but without specifying the name in the for loop. So, you can use operator.itemgetter like this
from operator import itemgetter
getter = itemgetter("toys")
print [getter(item) for item in prices_list]
# [25, 35]
The same can be used like this
total = 0
for item in prices_list:
total += getter(item)
for d in item_list:
for i in d.values():
print i
You could also call d.items() and get back tuples of (key, value) if you wanted access to both.
Additionally if you don't care about readability you could use this to get a list of all the values accross the dicts in your list.
[x for y in (i.values() for i in item_list) for x in y]
You can also use: [d for i in item_list for d in i.values()] to get the values and flatten them into a list (though yours would still be nested because of clothes' value being a list.
Finally you can merge the dictionaries into one if they have distinct keys:
joint_mapping = dict([d for i in item_list for d in i.items()])
or if they don't have unique values you can use the below code to produce a dict of lists of values. :
for k, v in [d for i in item_list for d in i.items()]:
new_d[k] = new_d.get(k, [])+[v]
You mentioned :
#Eenvincible - I would be inputting those values to other variable.
Print is just an example I gave. So would not want to hardcode the key.
I think you can do your trick using for loop as mentioned in other answers.
for item in item_list:
for key in item:
print item[key]
tempvariable = item[key]
.... do something here.
If you want to assign each value to a seperate variable, you should:
- First, assign that variables to any insignificant value EX: **None** or *""*
- Then, dynamically use the for loop to reassign each variable to new value using if condition or thier index.
EX:
price_toys , price_clothes , stock_toys , stock_clothes = None , None , None , None
for item in item_list:
for key in item:
tempvariable = item[key]
# condition to choose which varible will be set to the current value.
if item == "price" and key == "toys":
price_toys = tempvariable
# and so on

Categories

Resources