What's wrong with this code Python 3.3 - python

I'm trying to create a small Python program which calls a random student in lessons then it removes this student from the list till all other students are called.
Example :
ME
You
Others
I want to call randomly one and then remove it from the list so the next time it would be only
You
Others
I've wrote this code but it keeps repeating students without first calling all of them.
import random
klasa = {1 :'JOHN', 2 : 'Obama' , 3 : 'Michele' , 4 : 'Clinton'}
ran = []
random.seed()
l = random.randint(1,4)
while l not in ran:
ran.append(l)
print(klasa[l])
for x in ran:
if x != None:
ran.remove(x)
else:
break

There are two approaches you could take. One is to have a list of keys in the dictionary, select a key randomly from this list and then remove it. This would look like this:
from random import choice
keys = klasa.keys()
while keys: #while there are keys left in 'keys'
key = choice(keys) #get a random key
print("Calling %s" % (klasa.pop(key))) #get the value at that key, and remove it
keys.remove(key) #remove key from the list we select keys from
klasa.pop(key) will return the value associated with the key in addition to deleting it:
| pop(...)
| D.pop(k[,d]) -> v, remove specified key and return the corresponding value.
| If key is not found, d is returned if given, otherwise KeyError is raised
Another approach is to shuffle the list of keys before hand and go over each one, i.e:
from random import shuffle
keys = klasa.keys()
shuffle(keys) #put the keys in random order
for key in keys:
print("Calling %s" % (klasa.pop(key)))
If you want to delete people one person at time, you can simply do:
print("Calling %s" % klasa.pop(choice(klasa.keys())))
Though this means you will generate a list of the keys each time, it's better to store this in a list and remove the keys from this list as you delete them like in the first suggested method. keys = .keys() ... a_key = choice(keys), klasa.pop(key), keys.delete(key)
Note: In python 3.x you need to go keys = list(klasa) as .keys doesn't return a list like 2.x

I tried for simplicity:
>>> klasa = ['JOHN', 'Obama' , 'Michele' , 'Clinton']
>>> random.seed()
>>> l = len(klasa)
>>> while l > 0:
... i = random.randint(0,l-1)
... print (klasa[i])
... del klasa[i]
... l=len(klasa)
...
Michele
JOHN
Obama
Clinton
>>>

Modify this Solution According to your Needs
from random import *
klasa = {1 :'JOHN', 2 : 'Obama' , 3 : 'Michele' , 4 : 'Clinton'}
#Picks a random Student from the Dictionary of Students
already_called_Student=klasa[randint(1,4)]
print "Selected Student is" , already_called_Student
total_Students = len(klasa)
call_student_number = 0
while call_student_number < total_Students:
random_student=klasa[randint(1,4)]
if random_student == already_called_Student:
continue
print random_student
call_student_number = call_student_number + 1

Related

Find the most occurring character in a string

This piece of code is going to find the most occurring chr in a string ,and it almost works fine through a dictionary ,but unfortunately the problem is that I want to make it return the last key when there are two keys with the same frequency ,but it returns the first one.
And this is what I have done so far:
def most_frequent_letter(s):
st = s.lower().replace(' ', '')
frequencies = {}
for items in st:
if items in frequencies:
frequencies[items] += 1
else:
frequencies[items] = 1
return max(frequencies, key=frequencies.get)
most_frequent_letter('mmmaaa')
Out[48]: 'm'
However I don't know how to return 'a' instead of 'm'.
Here's a way that creates a reverse frequency dictionary. I also made the creation of the frequency dictionary and its reverse fairly succinct by using a dictionary comprehension:
def most_frequent_letter(s):
st = s.lower().replace(' ', '')
frequencies = {}
frequencies = {item: frequencies.setdefault(item, 0) + 1 for item in st}
rev_freq = {count: key for key, count in frequencies.items()}
return rev_freq[max(rev_freq)]
print(most_frequent_letter('nnmmmaaa')) # -> a
Python max function always returns the first maximum occurrence.
Hence, if you always want the last key, then you can just reverse the original string in your code.
def most_frequent_letter(s):
st = s.lower().replace(' ', '')
st = st[::-1]
frequencies = {}
for items in st:
if items in frequencies:
frequencies[items] += 1
else:
frequencies[items] = 1
return max(frequencies, key=frequencies.get)
Or sort the string first if you want the lowest valued key.
You can also just create your own max function instead to suit your needs.
def most_frequent_letter(word):
letters = list(word)
return (max(set(letters), key = letters.count))
print(most_frequent_letter('mmmaaa'))
# output:m
print(most_frequent_letter('some apples are green'))
# output: e
max() will return the highest value in a list. The key argument takes a single argument function to customize the sort order, in this case, it’s letters.count. The function is applied to each item on the iterable.
letters.count is a built-in function of list. It takes an argument and will count the number of occurrences for that argument. So letters.count('m') will return 3 and letters.count(a) returns 3.
set(test) returns all the unique values from test, so {3, 3}
So what we do in this single line of code is take all the unique values of test, which is {1, 3}. Next, max will apply the list.count function to them and return the maximum value.
collections library has Counter which does the job for you: We normalize the word with lower casing and replace space before reverse string to have last appearance first.
from collections import Counter
word = 'mmmaaa'
characters = Counter(reversed(word.lower().replace(' ', '')))
# most common
print(characters.most_common(1))
yes you can get both m and a, it depends how you want to get the output but I have taken a string just for example
def most_frequent_letter(s):
st = s.lower().replace(' ', '')
frequencies = {}
for items in st:
if items in frequencies:
frequencies[items] += 1
else:
frequencies[items] = 1
max_val=max(frequencies.values())
result=""
for key,value in frequencies.items():
if value==max_val:
result+=key
return result
result=most_frequent_letter('mmmaaa')
print(result)
the output will be "ma"
In python when you use the max function it will return the first max frequency, if you want the second max frequency you could try to delete from the list the 'm's so after that the first max frequency will be 'a'.

I just want to print the key, not their index

I need to print the keys + their values and it always prints the index of the key too, how can I fix that?
def task_3_4(something:str):
alphabet =list(string.ascii_letters)
i = 0
k=0
while i < len(alphabet):
dicts = {alphabet[i]: 0}
count = something.count(alphabet[i])
dicts[i] = count
if 0 < count:
for k in dicts:
print(k)
i = i+1
Based on the code it seems like you are trying to do some sort of counter of different characters in the string?
There is no index. your "index" is the "i" iterator you are using for your while loop. This simply makes a new key in dicts as called by dicts[i]. Thus when you call the print loop, it just iterates through and reads out I as well.
Try:
dicts[alphabet[i]] = count
Also your print function only prints out the key of the dict entry instead of the key-value pair. to do that you can try:
for k in dicts:
print(k,dicts[k])
Try reading up on the python docs for dicts.
https://docs.python.org/3/tutorial/datastructures.html

Python iterating and dictionary

I'm trying to create a dictionary with keys that are every 3 items of the list such as..(a,b,c) then (b,c,d) then (c,d,e) and the value of each key is the direct next letter, so the value for (a,b,c) would be d and the value for (b,c,d) would be e. This is the code I have so far but the problem is when i becomes 2, the dict tries to append L(2+3) which result in an index error. Also..if num is = 4 instead of 3, then my code wouldn't work. Can I get some hints as to what I should do to fix these 2 problems. Thank you
L = ['a','b','c','d','e']
num = 3
for i in range(len(L)-2):
a_dict[tuple(L[i:i+num])].append(L[i+num])
You can do it in one line like this:
import string
letters = string.ascii_lowercase
num = 3
mydict = {tuple(letters[i:i+num]):letters[i+num] for i in range(len(letters)-num)}
Here string.ascii_lowercase is a string containing English alphabet in lowercase. You can treat it same way as a list (slicing, element selection).
You are supposed to use "=" not ".append()" to add to dictionaries, i.e.
dictionary[key] = value
Also, you can use a modulus (%) to make sure that the list indexes do not get above the length of the list.
This slightly modified code works, including when num = 4 or num = 2 :
L = ['a','b','c','d','e']
num = 3
a_dict = {}
for i in range(len(L) - num):
a_dict[tuple(L[i:(i+num)%len(L)])] = (L[(i+num)%len(L)])
I hope that this is helpful for you.

Python: Sort a list according to two attributes

I have a list of the following kind:
class Ind(object):
def __init__(self,ID,mate):
self.ID=ID
self.mate=mate
population=[Ind(8,None), Ind(1,2), Ind(20,3), Ind(2,1), Ind(12,None), Ind(3,20), Ind(10,11), Ind(11,10)]
You can think of this list population as a population of individuals which all have an ID. Some of them have a mate (an individual who is present in the same population or the same list). The mate value is actually the ID of the mate! Therefore, if there is an instance of Ind which attributes ID equals 12 and mate equals 34, then there is necessarily an individual in the list whose ID equals 34 and whose mate equals 12. Individuals that do not have a mate have None in the mateattribute. Does it make sense?
I'd like to sort this list so that the first individual mates with the last one, the second individual mates with the second-to-last individual, etc... The individual which attribute mateequals None should stand in the middle of the list.
There are many possible outputs that fit what I want. Here is one example of these outputs for the above list:
population=[Ind(1,2), Ind(20,3), Ind(10,11), Ind(8,None), Ind(12,None), Ind(11,10), Ind(3,20), Ind(2,1)]
You can try something like this:
def custom_sort(population):
pop_dict = { ind.ID: ind for ind in population }
start = []
nones = []
end = []
for ind in population:
if ind.mate is None:
nones.append(ind)
elif pop_dict[ind.mate] not in start:
start.insert(0, ind)
end.append(pop_dict[ind.mate])
return start + nones + end
This is under assumption that "being a mate" is a 1-to-1 relation.
You just need a key for the sorting function. The following example requires that individuals are monogamous and not married to themselves. It also requires that if (a,b) is listed, (b,a) is also listed. If these prerequisites are not met and Ind(2,1) can occur without Ind(1,2), this function will place Ind(2,1) towards the end of the list. The first index in the key function is the type: "first" in relationship (where IDmate) comes third. These first and second types are sorted in order by their ids; last type is sorted in reverse order by its mate.
def keyfun(x):
if x.mate==None:
return (1,x.ID)
elif x.ID<x.mate:
return (0,x.ID)
else:
return (2,-x.mate)
sorted(population,key=keyfun)
Another way to handle this, still assuming that if (a,b) is in the list (b,a) will also be in the list, is to just preprocess by removing (b,a) cases, then postprocess by adding them back in in reverse order.
How about this. Split list into three lists, one with ID < mate, the second with ID > mate, and the third with mate is None. Then, concatenate the sorted lists, each sorted via ID.
I've added a __repr__ method to the Ind class for output readability.
class Ind(object):
def __init__(self,ID,mate):
self.ID=ID
self.mate=mate
def __repr__(self):
return 'Ind({},{})'.format(self.ID,self.mate)
population=[Ind(8,None), Ind(1,2), Ind(2,3), Ind(2,1), Ind(12,None), Ind(3,2), Ind(10,11), Ind(11,10)]
def custom_sort(pop):
singles, less, more = [], [], []
for p in pop:
if p.mate is None:
singles.append(p)
elif p.ID < p.mate:
less.append(p)
elif p.ID > p.mate:
more.append(p)
comp = lambda x,y: cmp(x.ID,y.ID)
return sorted(less,cmp=comp) + sorted(singles,cmp=comp) + sorted(more,cmp=comp,reverse=True)
print custom_sort(population)
This outputs:
[Ind(1,2), Ind(2,3), Ind(10,11), Ind(8,None), Ind(12,None), Ind(11,10), Ind(3,2), Ind(2,1)]
There is a lot you can do with costum key functions:
def my_key(ind):
if ind.mate is None:
return 0
if ind.ID < ind.mate:
return -ind.ID - 1
else:
return ind.mate + 1
population.sort(key=my_key)
This assumes that IDs will never be negative. If IDs are always greater than 0, you can discard the - 1 and + 1.

Python iterator question

I have this list:
names = ['john','Jonh','james','James','Jardel']
I want loop over the list and handle consecutive names with a case insensitive match in the same iteration. So in the first iteration I would do something with'john' and 'John' and I want the next iteration to start at 'james'.
I can't think of a way to do this using Python's for loop, any suggestions?
This would be one for itertools.groupby, which groups consecutive equal elements from a list or other iterable. you can specify a function to do the comparison, so that, in your case, the same name in different cases can still be counted as the same thing.
for k, g in itertools.groupby(names, lambda s: s.lower()):
# Example: in the first iteration:
# k = "john"
# g = an iterator over ["john", "John"]
# Process them as you like
names = ['john','John','james','James']
for name, capitalized_name in zip(names[::2], names[1::2]):
print name, capitalized_name
Note that you need an even amount of items for this to work properly.
Or (maybe better; hard to tell with little context) use a set to filter the list to contain only unique names (note that this loses order):
>>> names = ['john','John','james','James','Jardel']
>>> unique_names = set([x.lower() for x in names])
>>> for unique_name in unique_names:
... print unique_name
...
jardel
james
john
You could just use a while loop:
i = 0
while i < len(names):
# compare names[i] with names[i + 1]
i = i + 2 # or + 1 if names not equal, for example
Or are you looking for something a bit more involved?
As you iterate thru the loop, you could try keeping track of the previous name in the list. At the same time, when you're going to store the names, you can make a call to lower() or capitalize() to make the formatting of each name consistent so that you can compare them easier.
e.g.
first = True
prev= ""
for name in names:
if first: #First iteration
prev = name.lower() #Need to get the first elem
do_something_to(curr)
first = False
else:
if prev == name.lower():
print "do nothing"
else:
do_something_to(curr)
prev = name.lower()
May not be the most efficient, but works.
My $0.02:
def byPairs(li):
for i in xrange(1, len(li), 2):
yield (li[i-1], li[i])
for a,b in byPairs(names):
if a.lower()==b.lower():
doSomething(a,b)
I'm not sure I understood the question exactly; what are you trying to accomplish?

Categories

Resources