Related
I'm adding a function to a class to output the item with the lowest price but what I'm getting are all the prices. See photo and code. What am I missing on the code?
def get_low_price(self):
self.get_total_toys()
#To check if toybox is empty or not
if self.total > 0:
msg = f'The toy box contains {self.total} toys\n'
for a_toy in self.all_toys:
self.get_total_cost()
msg += f'A {(a_toy.colour).lower()} {a_toy.name} which cost ${a_toy.price:.2f}\n'
for i in [a_toy.price]:
i = ([i])
print(min(i))
return f'{msg}Total cost: ${self.cost_total:.2f}'
This inner loop isn't doing anything useful:
for i in [a_toy.price]:
i = ([i])
print(min(i))
Here a_toy is already just a single toy. Looping over a new list containing only its price doesn't accomplish anything you could get just by accessing a_toy.price directly, and rebinding the loop variable i to another new list (in extraneous parentheses) doesn't add anything.
I think you want to move all of the min-finding logic outside of the earlier loop, unless you want to compare prices yourself. Instead, you can use just one min call, outside of the loop:
for a_toy in self.all_toys: # don't include the stuff below in this loop
...
cheapest = min(self.all_toys, key=lambda t: t.price) # find cheapest
# do something down here with cheapest, or cheapest.name, maybe
I didn't understand what exactly you are trying to do using the for loop in that method. If you are thinking that i = ([i]) is going to append price to a list then it's wrong. Use the below logic and rewrite your method. It will work.
toys = {"doll": 5, "hulk": 10, "teddy": 15}
cheapest_toy_name = ""
cheapest_toy_price = float("inf")
for k, v in toys.items():
if cheapest_toy_price > v:
cheapest_toy_price = v
cheapest_toy_name = k
print(cheapest_toy_name)
I have been working on this code for quite a while now and frankly, I have no more ideas on how to solve this. I have sought different threads on how to do this, unfortunately, still have no answers.
To start off, I have this pool of data that is a string but needs to be considered as a list. For example:
# empDataLT
200401003,Luisa,Jurney,Accounting,800,21,4/8/2002,;
200208006,Clorinda,Heimann,Accounting,1050,15,5/21/1994,;
200307014,Dick,Wenzinger,Admin,565,15,10/13/1973,;
200901005,Ahmed,Angalich,Purchasing,750,20,2/10/1973,;
200704013,Iluminada,Ohms,Marketing,750,16,7/13/1972,;
201701018,Joanna,Leinenbach,Finance,1050,15,11/6/1980,;
201003007,Caprice,Suell,Admin,750,18,6/28/1992
a = empRecords.strip().split(";")
This pool is in the format: Employee Number, First name, Last Name, Department, Rate per day, No. of Days Worked, Birthdate
What I have been trying to do is to compute the employees' rate per day multiplied to the number of days worked, then find which of them is the highest earning employee. I have the following code which works decent, but of course it lacks the latter result needed (aka, the highest earning).
import empDataLT as x
def earn():
empEarn = list() # convert module to a list
for er in x.a:
empErn = er.strip().split(",")
empEarn.append(empErn)
b = sorted(empEarn, key=lambda x: x[4])
for e in b:
ern = (int(e[4]) * int(e[5]))
print(ern)
This will result to something like this:
20800
14400
21600
24000
12800
24000
Which is great because I have the result (yay). However, I am unable to look for the highest earning as I usually get error when I try max() since it's an integer. I tried converting it to a str then use max() and it will just give me the highest number per integer.
I'm not really sure what to do anymore.
Try this :
empRecords = '''200401003,Luisa,Jurney,Accounting,800,21,4/8/2002,;
200208006,Clorinda,Heimann,Accounting,1050,15,5/21/1994,;
200307014,Dick,Wenzinger,Admin,565,15,10/13/1973,;
200901005,Ahmed,Angalich,Purchasing,750,20,2/10/1973,;
200704013,Iluminada,Ohms,Marketing,750,16,7/13/1972,;
201701018,Joanna,Leinenbach,Finance,1050,15,11/6/1980,;
201003007,Caprice,Suell,Admin,750,18,6/28/1992'''
a = empRecords.strip().split(";")
earn = []
for i in a:
t = i.split(',')
cur = int(t[4])*int(t[5])
earn.append(cur)
print(cur)
print("Maximum Earning :",max(earn))
You can use max, you just need to keep the previous largest-found:
highestEarning = 0
for e in b:
highestEarning = max((int(e[4]) * int(e[5])),highestEarning)
Once the for loop is done, highestEarning will be the highest earning in the list.
max() takes an iterable, for example a list.
You probably tried to run max() on a single int, which doesn't work, because how should it? Finding the maximum value of one value is not really a thing.
You can create a list of ernand then use max() on this list.
You might use yield keyword for your task following way:
import empDataLT as x
def earn():
empEarn = list() # convert module to a list
for er in x.a:
empErn = er.strip().split(",")
empEarn.append(empErn)
b = sorted(empEarn, key=lambda x: x[4])
for e in b:
ern = (int(e[4]) * int(e[5]))
print(ern)
yield ern
highest = max(earn())
For discussion of yield and how to use I suggest realpython tutorial
bank = {'id':{'moneys':{}}}
with open(bankID.txt, 'r') as a:
for key in a.readlines():
bank['id']=[str(key)[0:3]]
with open(banknumbers.txt) as b:
for value in b.readlines():
bankID = value[0:3]
bankMoney = int(value[4:9])
if bank['id'] == bankID:
bank['id]['money']= bankMoney
#bank[bankID] = bankMoney <--this one kinda works but it isnt right.
#an example of the 2nd txt file would look like
#234 8763 so id number and money respectively
How come my first forloop does not assign all my id with a key. when I print (bank['id']) the only value that comes out is the first key read.
In my first txt file it only has the bank id and the second txt file has the bank id and the amount of money after it. I cant seem to figure out how to compare the bank id of my dictionary and the bank id of the 2nd txt file and assign the value for money.
So I want the output to look like {'875': 234,'576': 345, '676': 8837}
How come my first forloop does not assign all my id with a key.
Because you're telling it to assign the key "id" the value [str(key)[0:3]]
So you wind up with exactly one key, the string literal "id" and whatever the last value of [str(key)[0:3]] was.
You don't actually need a dictionary within a dictionary unless you have 3 values to keep track of, like a bankId, transactionId and amount.
Suppose banknumbers.txt looked like this instead:
# 234 8763 44.55 -- so a bank number, a transaction number and some money.
This would give you a dictionary within a dictionary for that:
bank = {}
with open(bankID.txt, 'r') as a:
for key in a.readlines():
bank[str(key)[0:3]] = {}
with open(banknumbers.txt) as b:
for value in b.readlines():
bankID = value[0:3]
transactionId = int(value[4:9])
money = float(value(9:19))
bank[bankID][transactionId] = money
You should probably go read the relevant section of the python tutorial: https://docs.python.org/2/tutorial/datastructures.html#dictionaries
I think you're misunderstanding dictionaries. You're overwriting bank['id'] every iteration in the for loop. I'm not exactly sure what you want, but you may want to have a list of banks. The code would be:
bank = []
with open(bankID.txt, 'r') as a:
for key in a.readlines():
bank.append({'id':[str(key)[0:3]], 'moneys': 0})
with open(banknumbers.txt) as b:
for value in b.readlines():
bankID = value[0:3]
bankMoney = int(value[4:9])
for bnk in bank:
if bnk['id'] == bankID:
bnk['money'] = bankMoney
First: you don't have to code this for me, unless you're a super awesome nice guy. But since you're all great at programming and understand it so much better than me and all, it might just be easier (since it's probably not too many lines of code) than writing paragraph after paragraph trying to make me understand it.
So - I need to make a list of high scores that updates itself upon new entries. So here it goes:
First step - done
I have player-entered input, which has been taken as a data for a few calculations:
import time
import datetime
print "Current time:", time1.strftime("%d.%m.%Y, %H:%M")
time1 = datetime.datetime.now()
a = raw_input("Enter weight: ")
b = raw_input("Enter height: ")
c = a/b
Second step - making high score list
Here, I would need some sort of a dictionary or a thing that would read the previous entries and check if the score (c) is (at least) better than the score of the last one in "high scores", and if it is, it would prompt you to enter your name.
After you entered your name, it would post your name, your a, b, c, and time in a high score list.
This is what I came up with, and it definitely doesn't work:
list = [("CPU", 200, 100, 2, time1)]
player = "CPU"
a = 200
b = 100
c = 2
time1 = "20.12.2012, 21:38"
list.append((player, a, b, c, time1))
list.sort()
import pickle
scores = open("scores", "w")
pickle.dump(list[-5:], scores)
scores.close()
scores = open("scores", "r")
oldscores = pickle.load(scores)
scores.close()
print oldscores()
I know I did something terribly stupid, but anyways, thanks for reading this and I hope you can help me out with this one. :-)
First, don't use list as a variable name. It shadows the built-in list object. Second, avoid using just plain date strings, since it is much easier to work with datetime objects, which support proper comparisons and easy conversions.
Here is a full example of your code, with individual functions to help divide up the steps. I am trying not to use any more advanced modules or functionality, since you are obviously just learning:
import os
import datetime
import cPickle
# just a constants we can use to define our score file location
SCORES_FILE = "scores.pickle"
def get_user_data():
time1 = datetime.datetime.now()
print "Current time:", time1.strftime("%d.%m.%Y, %H:%M")
a = None
while True:
a = raw_input("Enter weight: ")
try:
a = float(a)
except:
continue
else:
break
b = None
while True:
b = raw_input("Enter height: ")
try:
b = float(b)
except:
continue
else:
break
c = a/b
return ['', a, b, c, time1]
def read_high_scores():
# initialize an empty score file if it does
# not exist already, and return an empty list
if not os.path.isfile(SCORES_FILE):
write_high_scores([])
return []
with open(SCORES_FILE, 'r') as f:
scores = cPickle.load(f)
return scores
def write_high_scores(scores):
with open(SCORES_FILE, 'w') as f:
cPickle.dump(scores, f)
def update_scores(newScore, highScores):
# reuse an anonymous function for looking
# up the `c` (4th item) score from the object
key = lambda item: item[3]
# make a local copy of the scores
highScores = highScores[:]
lowest = None
if highScores:
lowest = min(highScores, key=key)
# only add the new score if the high scores
# are empty, or it beats the lowest one
if lowest is None or (newScore[3] > lowest[3]):
newScore[0] = raw_input("Enter name: ")
highScores.append(newScore)
# take only the highest 5 scores and return them
highScores.sort(key=key, reverse=True)
return highScores[:5]
def print_high_scores(scores):
# loop over scores using enumerate to also
# get an int counter for printing
for i, score in enumerate(scores):
name, a, b, c, time1 = score
# #1 50.0 jdi (20.12.2012, 15:02)
print "#%d\t%s\t%s\t(%s)" % \
(i+1, c, name, time1.strftime("%d.%m.%Y, %H:%M"))
def main():
score = get_user_data()
highScores = read_high_scores()
highScores = update_scores(score, highScores)
write_high_scores(highScores)
print_high_scores(highScores)
if __name__ == "__main__":
main()
What it does now is only add new scores if there were no high scores or it beats the lowest. You could modify it to always add a new score if there are less than 5 previous scores, instead of requiring it to beat the lowest one. And then just perform the lowest check after the size of highscores >= 5
The first thing I noticed is that you did not tell list.sort() that the sorting should be based on the last element of each entry. By default, list.sort() will use Python's default sorting order, which will sort entries based on the first element of each entry (i.e. the name), then mode on to the second element, the third element and so on. So, you have to tell list.sort() which item to use for sorting:
from operator import itemgetter
[...]
list.sort(key=itemgetter(3))
This will sort entries based on the item with index 3 in each tuple, i.e. the fourth item.
Also, print oldscores() will definitely not work since oldscores is not a function, hence you cannot call it with the () operator. print oldscores is probably better.
Here are the things I notice.
These lines seem to be in the wrong order:
print "Current time:", time1.strftime("%d.%m.%Y, %H:%M")
time1 = datetime.datetime.now()
When the user enters the height and weight, they are going to be read in as strings, not integers, so you will get a TypeError on this line:
c = a/b
You could solve this by casting a and b to float like so:
a = float(raw_input("Enter weight: "))
But you'll probably need to wrap this in a try/catch block, in case the user puts in garbage, basically anything that can't be cast to a float. Put the whole thing in a while block until they get it right.
So, something like this:
b = None
while b == None:
try:
b = float(raw_input("Enter height: "))
except:
print "Weight should be entered using only digits, like '187'"
So, on to the second part, you shouldn't use list as a variable name, since it's a builtin, I'll use high_scores.
# Add one default entry to the list
high_scores = [("CPU", 200, 100, 2, "20.12.2012, 4:20")]
You say you want to check the player score against the high score, to see if it's best, but if that's the case, why a list? Why not just a single entry? Anyhow, that's confusing me, not sure if you really want a high score list, or just one high score.
So, let's just add the score, no matter what:
Assume you've gotten their name into the name variable.
high_score.append((name, a, b, c, time1))
Then apply the other answer from #Tamás
You definitely don't want a dictionary here. The whole point of a dictionary is to be able to map keys to values, without any sorting. What you want is a sorted list. And you've already got that.
Well, as Tamás points out, you've actually got a list sorted by the player name, not the score. On top of that, you want to sort in downward order, not upward. You could use the decorate-sort-undecorate pattern, or a key function, or whatever, but you need to do something. Also, you've put it in a variable named list, which is a very bad idea, because that's already the name of the list type.
Anyway, you can find out whether to add something into a sorted list, and where to insert it if so, using the bisect module in the standard library. But it's probably simpler to just use something like SortedCollection or blist.
Here's an example:
highscores = SortedCollection(scores, key=lambda x: -x[3])
Now, when you finish the game:
highscores.insert_right((player, a, b, newscore, time1))
del highscores[-1]
That's it. If you were actually not in the top 10, you'll be added at #11, then removed. If you were in the top 10, you'll be added, and the old #10 will now be #11 and be removed.
If you don't want to prepopulate the list with 10 fake scores the way old arcade games used to, just change it to this:
highscores.insert_right((player, a, b, newscore, time1))
del highscores[10:]
Now, if there were already 10 scores, when you get added, #11 will get deleted, but if there were only 3, nothing gets deleted, and now there are 4.
Meanwhile, I'm not sure why you're writing the new scores out to a pickle file, and then reading the same thing back in. You probably want to do the reading before adding the highscore to the list, and then do the writing after adding it.
You also asked how to "beautify the list". Well, there are three sides to that.
First of all, in the code, (player, a, b, c, time1) isn't very meaningful. Giving the variables better names would help, of course, but ultimately you still come down to the fact that when accessing list, you have to do entry[3] to get the score or entry[4] to get the time.
There are at least three ways to solve this:
Store a list (or SortedCollection) of dicts instead of tuples. The code gets a bit more verbose, but a lot more readable. You write {'player': player, 'height': a, 'weight': b, 'score': c, 'time': time1}, and then when accessing the list, you do entry['score'] instead of entry[3].
Use a collection of namedtuples. Now you can actually just insert ScoreEntry(player, a, b, c, time1), or you can insert ScoreEntry(player=player, height=a, weight=b, score=c, time=time1), whichever is more readable in a given case, and they both work the same way. And you can access entry.score or as entry[3], again using whichever is more readable.
Write an explicit class for score entries. This is pretty similar to the previous one, but there's more code to write, and you can't do indexed access anymore, but on the plus side you don't have to understand namedtuple.
Second, if you just print the entries, they look like a mess. The way to deal with that is string formatting. Instead of print scores, you do something like this:
print '\n'.join("{}: height {}, weight {}, score {} at {}".format(entry)
for entry in highscores)
If you're using a class or namedtuple instead of just a tuple, you can even format by name instead of by position, making the code much more readable.
Finally, the highscore file itself is an unreadable mess, because pickle is not meant for human consumption. If you want it to be human-readable, you have to pick a format, and write the code to serialize that format. Fortunately, the CSV format is pretty human-readable, and most of the code is already written for you in the csv module. (You may want to look at the DictReader and DictWriter classes, especially if you want to write a header line. Again, there's the tradeoff of a bit more code for a lot more readability.)
I am doing a problem where I need to use data from a csv file to find which film has the high gross total for each year.
I already have the dict 'year' with each film and the year it came out and the same for 'gross'.
Despite this, my code is still returning 0 as the max gross. What am I missing here?
def MaxGrossFinder(c):
for film in year:
MaxGross = 0
f = int(gross[film])
if year[film] == c:
if f > MaxGross:
MaxGross = f
return MaxGross
Use the max() function. It does this operation correctly.
max(int(gross[film]) for film in year if year[film] == c)
Your problem is that you set MaxGross to zero in each iteration. So all values but the last are ignored.
Please also look into creating a Film class and using one dict of film objects, rather than having multiple parallel dicts.
Take MaxGross out of the for-loop:
def MaxGrossFinder(c):
MaxGross = 0
for film in year:
f = int(gross[film])
if year[film] == c:
if f > MaxGross:
MaxGross = f
return MaxGross
With MaxGross = 0 inside the for-loop, all the prior iterations mean nothing. Only the last would affect MaxGross. That's probably not the intention.
Another problem might occur if c and year[film] are floats. Don't compare floats for equality (unless you know what you are doing) since floats can have inexact representations. Instead
define some concept of nearness:
def near(a,b,rtol=1e-5,atol=1e-8):
return abs(a-b)<(atol+rtol*abs(b))
and compare if near(year[film],c).
You can use max() directly,
for film in year:
if year[film]==c:
print(max(int(gross[film]))