How can I go through different dictionaries using nested loop? - python-3.x

sem1_credit = {'A': 4, 'B': 4, 'C': 3}
sem2_credit = {'D': 5, 'E': 1}
sem3_credit = {'F': 3}
e = 2
for j in range(e):
for i in 'sem'+str(j+1)+'_credit':
I wanted to use loop to access different dict. So I tried to create the dict name with concatenation using loop. But it doesn't work. Is there a way to work it out or is there some other way to approach dict without loops.

You can get a dictionary of the current local symbols table by calling locals(). So locals()['sem1_credit'] is essentially this sem1_credit.
From here, you can build a loop:
sem1_credit = {'A': 4, 'B': 4, 'C': 3}
sem2_credit = {'D': 5, 'E': 1}
sem3_credit = {'F': 3}
for idx in range(1, 4):
credits = locals()[f'sem{idx}_credit']
for key, credit in credits.items():
print(f"{key} {credit}")
Keep in mind that the range(num) generate numbers from 0 to num-1. So in your code, range(2) only generates 0 and 1.

Related

Is there a way for me to use a dictionary values to loop through a string?

What I'm trying to do here is change the values in the dictionary and then use the dictionary so that it loops through the string's elements so that it'll reflect for every instance of a key. Do any of you have a recommendation on what to do next? I think I need to set up a loop, but am unsure of what to do. Thank you.
string = 'abdcabdcadcbacb'
b = 12
dict = {'a': 50, 'b': 40, 'c': 30, 'd': 20}
for aa in dict.keys():
dict[i] -= b
#output should be 360
Edit: I also need to return the sum of the difference once it's looped through the string. The output should be 360, sorry for not making it clear.
I believe you need.
string = 'abdcabdcadcbacb'
b = 12
d = {'a': 50, 'b': 40, 'c': 30, 'd': 20}
for i in string:
if i in d:
d[i] -= b
print(d) # {'a': 2, 'b': -8, 'c': -18, 'd': -16}
If I understand your question you want to modify your dictionary first by subtracting your dictionary value with b. Then you expect the sum by iterating your string. If any character of the string matches to dictionary key then sum it.
string = 'abdcabdcadcbacb'
b = 12
sum = 0
my_dict = {'a': 50, 'b': 40, 'c': 30, 'd': 20}
for k in my_dict.keys():
my_dict[k] -= b
for s in string:
if s in my_dict.keys():
sum += my_dict[s]
print(sum)
Output
360
dont use reserved words as variable names.
In your code, you have used dict as a variable name which is a reserved keyword.
string = 'abdcabdcadcbacb'
b = 12
dictionary = {'a': 50, 'b': 40, 'c': 30, 'd': 20}
for i in dictionary.keys():
dictionary[i]-=b
print(dictionary)

Python get random value when using min() when there is more than one match

I have the following code:
dict = {'a': 1, 'b': 2, 'c': 5, 'd':1, 'e': 5, 'f': 1}
get_min = min(dict, key=dict.get)
As you can see here there is actually three min() matches "a", "d" and "f" with the value of 1.
This will return "a" 100% of the time as that is what min() is designed to do from what I am reading. However, I have a need where I would like to randomly get back "a", "d" or "f" instead of just "a".
Is there a way I can do this using min() or some other way (maybe lambda, I am not very good at it)? I thought this would be pretty simple but it turns out it is not :P. I can get this working with some for loops and creating lists but I was looking for the shortest way possible.
Your thoughts would be appreciated!
Thanks,
jAC
Here is one solution:
import random
dic = {'a': 1, 'b': 2, 'c': 5, 'd':1, 'e': 5, 'f': 1}
a = random.choice([k for k,v in dic.items() if v == min(dic.values())])
np.random.choice([k for k in dict.keys() if dict[key]==min(dict.values())])

Can anyone help me find a reason why my function, after return, changes the values of the dict I am passing? [duplicate]

While reading up the documentation for dict.copy(), it says that it makes a shallow copy of the dictionary. Same goes for the book I am following (Beazley's Python Reference), which says:
The m.copy() method makes a shallow
copy of the items contained in a
mapping object and places them in a
new mapping object.
Consider this:
>>> original = dict(a=1, b=2)
>>> new = original.copy()
>>> new.update({'c': 3})
>>> original
{'a': 1, 'b': 2}
>>> new
{'a': 1, 'c': 3, 'b': 2}
So I assumed this would update the value of original (and add 'c': 3) also since I was doing a shallow copy. Like if you do it for a list:
>>> original = [1, 2, 3]
>>> new = original
>>> new.append(4)
>>> new, original
([1, 2, 3, 4], [1, 2, 3, 4])
This works as expected.
Since both are shallow copies, why is that the dict.copy() doesn't work as I expect it to? Or my understanding of shallow vs deep copying is flawed?
By "shallow copying" it means the content of the dictionary is not copied by value, but just creating a new reference.
>>> a = {1: [1,2,3]}
>>> b = a.copy()
>>> a, b
({1: [1, 2, 3]}, {1: [1, 2, 3]})
>>> a[1].append(4)
>>> a, b
({1: [1, 2, 3, 4]}, {1: [1, 2, 3, 4]})
In contrast, a deep copy will copy all contents by value.
>>> import copy
>>> c = copy.deepcopy(a)
>>> a, c
({1: [1, 2, 3, 4]}, {1: [1, 2, 3, 4]})
>>> a[1].append(5)
>>> a, c
({1: [1, 2, 3, 4, 5]}, {1: [1, 2, 3, 4]})
So:
b = a: Reference assignment, Make a and b points to the same object.
b = a.copy(): Shallow copying, a and b will become two isolated objects, but their contents still share the same reference
b = copy.deepcopy(a): Deep copying, a and b's structure and content become completely isolated.
Take this example:
original = dict(a=1, b=2, c=dict(d=4, e=5))
new = original.copy()
Now let's change a value in the 'shallow' (first) level:
new['a'] = 10
# new = {'a': 10, 'b': 2, 'c': {'d': 4, 'e': 5}}
# original = {'a': 1, 'b': 2, 'c': {'d': 4, 'e': 5}}
# no change in original, since ['a'] is an immutable integer
Now let's change a value one level deeper:
new['c']['d'] = 40
# new = {'a': 10, 'b': 2, 'c': {'d': 40, 'e': 5}}
# original = {'a': 1, 'b': 2, 'c': {'d': 40, 'e': 5}}
# new['c'] points to the same original['d'] mutable dictionary, so it will be changed
It's not a matter of deep copy or shallow copy, none of what you're doing is deep copy.
Here:
>>> new = original
you're creating a new reference to the the list/dict referenced by original.
while here:
>>> new = original.copy()
>>> # or
>>> new = list(original) # dict(original)
you're creating a new list/dict which is filled with a copy of the references of objects contained in the original container.
Adding to kennytm's answer. When you do a shallow copy parent.copy() a new dictionary is created with same keys,but the values are not copied they are referenced.If you add a new value to parent_copy it won't effect parent because parent_copy is a new dictionary not reference.
parent = {1: [1,2,3]}
parent_copy = parent.copy()
parent_reference = parent
print id(parent),id(parent_copy),id(parent_reference)
#140690938288400 140690938290536 140690938288400
print id(parent[1]),id(parent_copy[1]),id(parent_reference[1])
#140690938137128 140690938137128 140690938137128
parent_copy[1].append(4)
parent_copy[2] = ['new']
print parent, parent_copy, parent_reference
#{1: [1, 2, 3, 4]} {1: [1, 2, 3, 4], 2: ['new']} {1: [1, 2, 3, 4]}
The hash(id) value of parent[1], parent_copy[1] are identical which implies [1,2,3] of parent[1] and parent_copy[1] stored at id 140690938288400.
But hash of parent and parent_copy are different which implies
They are different dictionaries and parent_copy is a new dictionary having values reference to values of parent
"new" and "original" are different dicts, that's why you can update just one of them.. The items are shallow-copied, not the dict itself.
In your second part, you should use new = original.copy()
.copy and = are different things.
Contents are shallow copied.
So if the original dict contains a list or another dictionary, modifying one them in the original or its shallow copy will modify them (the list or the dict) in the other.

Python Groupby keys list of dictionaries

I have the following for instance:
x = [{'A':1},{'A':1},{'A':2},{'B':1},{'B':1},{'B':2},{'B':3},{'C':1},{'D':1}]
and I would like to get a dictionary like this:
x = [{'A': [1,2], 'B': [1,2,3], 'C':[1], 'D': [1]}]
Do you have any idea how I could get this please?
You could use a collections.defaultdict of sets to collect unique values, then convert the final result to a dictionary with values as lists using a dict comprehension:
from collections import defaultdict
lst = [{'A':1},{'A':1},{'A':2},{'B':1},{'B':1},{'B':2},{'B':3},{'C':1},{'D':1}]
result = defaultdict(set)
for dic in lst:
for key, value in dic.items():
result[key].add(value)
print({key: list(value) for key, value in result.items()})
Output:
{'A': [1, 2], 'B': [1, 2, 3], 'C': [1], 'D': [1]}
Although its probably better to add your data directly to the defaultdict to begin with, instead of creating a list of singleton dictionaries(don't recommend this data structure) then converting the result.
Using dict.setdefault
Ex:
x = [{'A':1},{'A':1},{'A':2},{'B':1},{'B':1},{'B':2},{'B':3},{'C':1},{'D':1}]
res = {}
for i in x:
for k, v in i.items():
res.setdefault(k, set()).add(v)
#or res = [{k: list(v) for k, v in res.items()}]
print(res)
Output:
{'A': {1, 2}, 'B': {1, 2, 3}, 'C': {1}, 'D': {1}}

counting letter frequency with a dict

I'm trying to find the frequency of letters without the Counter.And the code will output a dictionary form of result. And what I have done so far is to make the program count the word frequencies but not the letter/character frequencies. If anyone could point out my mistakes in this code that would be wonderful. Thank you.
It supposed to look like this:
{'a':2,'b':1,'c':1,'d':1,'z':1}
**but this is what I am actually getting:
{'abc':1,'az':1,'ed':1}
**my code is below
word_list=['abc','az','ed']
def count_letter_frequency(word_list):
letter_frequency={}
for word in word_list:
keys=letter_frequency.keys()
if word in keys:
letter_frequency[word]+=1
else:
letter_frequency[word]=1
return letter_frequency
Use collections.Counter
from collections import Counter
print Counter(''.join(word_list))
# Counter({'a': 2, 'c': 1, 'b': 1, 'e': 1, 'd': 1, 'z': 1})
Or count the elements yourself if you don't want to use Counter.
from collections import defaultdict
d = defaultdict(int)
for c in ''.join(word_list):
d[c] += 1
print d
# defaultdict(<type 'int'>, {'a': 2, 'c': 1, 'b': 1, 'e': 1, 'd': 1, 'z': 1})
This is the correct code:
word_list=['abc','az','ed']
def count_letter_frequency(word_list):
letter_frequency={}
for word in word_list:
for letter in word:
keys=letter_frequency.keys()
if letter in keys:
letter_frequency[letter]+=1
else:
letter_frequency[letter]=1
return letter_frequency
You were iterating over the list and the list contains words. So, you were making words as keys in your dictionary. So, you have to add another for loop to iterate over the letters in each word.
Would this be acceptable:
flat = ''.join(word_list)
{l: flat.count(l) for l in set(flat)}
#{'a': 2, 'b': 1, 'c': 1, 'd': 1, 'e': 1, 'z': 1}
If you would prefer this in for loop, here it goes:
flat = ''.join(word_list)
result = {}
for l in flat:
if l in result:
result[l] += 1
else:
result[l] = 1

Resources