Related
f = open('studMarks.txt', 'r')
marks = 0
# Sort out names, split the words then sort which order
for line in f:
words = line.split()
fname = words[0]
lname = words[1]
print(f"{lname},{fname}")
f.close()
f = open('studMarks.txt', 'r')
sum = 0
count = 0
for line in f:
count += 1
sum += float(line.split()[2])
n = []
average = sum/count
print(f"{average}")
When using the for loop it seems to display a value of 64.3, which I believe is for the total of the whole student list and average for all marks.
I need to produce the an output which displays the student names and average on the same line. I can do for the names but I cannot do it for the average as I keep getting errors. I don't know what to input in.
Below is the full solution. The with open line is a context manager and ensures that the file will get closed as soon as you exit the block. You should get used to using this style as it's the safe way to do I/O. The rest is just bog standard Python.
marks=dict()
with open('studMarks.txt', 'r') as f:
for line in f:
words = line.split()
fname = words[0]
lname = words[1]
score = int(words[2])
key = f'{fname} {lname}'
count_key = f'{fname} {lname}_count'
latest_score = score + (marks.get(key)[0] if marks.get(key) else 0)
latest_count = 1 + (marks.get(key)[1] if marks.get(key) else 0)
marks[key] = (latest_score, latest_count )
for name, value in marks.items():
print(f'{name} : {value[0]/value[1]}')
This is an interesting problem.
From what I understand you have a text file that looks like this:
Johnny Ly 90 100 Adam Best 80 30 Tim Smith 10 20 in a file called studentMarks2.txt
and want output like this:
Johnny_Ly 95.0 Adam_Best 55.0 Tim_Smith 15.0
if that is true then it can be done using code like this without pandas or csv
though those would make this a lot easier.
fileContents = []
with open('studMarks2.txt','r') as f:
fileContents = f.read().split()
students = dict()
names = []
for content in fileContents:
if content.isnumeric():
studentKey = '_'.join(names)
currentScore = students.get(studentKey,[])
newScore = currentScore + [float(content)]
students.update({studentKey:newScore})
else:
if len(names) == 2:
names.clear()
names.append(content)
else:
names.append(content)
for student,scores in students.items():
avg = sum(scores)/len(scores)
print(student,avg,end=' ')
Broken down
This part reads the contents and splits on white space
fileContents = []
with open('studMarks2.txt','r') as f:
fileContents = f.read().split()
this part then iterates through the contents
storing the names as keys in a dictionary and putting the scores in a list
students = dict()
names = []
for content in fileContents:
if content.isnumeric():
studentKey = '_'.join(names)
currentScore = students.get(studentKey,[])
newScore = currentScore + [float(content)]
students.update({studentKey:newScore})
else:
if len(names) == 2:
names.clear()
names.append(content)
else:
names.append(content)
Lastly it iterates over the dictionary and output the avg on one line
for student,scores in students.items():
avg = sum(scores)/len(scores)
print(student,avg,end=' ')
I am trying to read a file and then have it print specific integers (Retail Prices) that I put into a dictionary from that file. This part I got. But when I try and print the Retail Prices outside of the loop, it cannot find them. If I include it within the loop, the file only reads one line! Help!!
while line != '':
line = fh.readline()
line = line.replace(',', '')
line = line.replace('"', '')
line = line.replace(';', ',')
linesplit = line.split(',')
linesplit = line.split(',')
dictionary = dict(zip(headerItems, linesplit))
RetailPrice = dictionary.get('retailprice')
print(RetailPrice)
RetailPrice = Retail_Prices
z = z + 1
if z == 4:
price = 'goodbye'
price = input('Enter a valid Retail Price to find all products below that price, or type anything else to exit: ')
print(Retail_Prices)
You can have the keyword dictionary outside the while loop set to None or empty dict and then over-ride it in the while loop.
I'm currently trying to make a Chromebook rental application for my high school that stores checkout information in a JSON file. Everything works except removing data from the JSON array. I found a YouTube video(link) that I thought would work as a solution, so I followed along with that. However, whenever there's more than two elements and I enter anything higher than two, it doesn't delete anything. Even worse, when I enter the number one, it deletes everything but the zero index(whenever the array has more than two elements in it).
Here's the Python code:
def view_data(): # Prints JSON Array to screen
with open(filename, "r") as f:
data = json.load(f)
i = 0
for item in data:
name = item["name"]
chromebook = item["chromebook"]
check_out = item["time&date"]
print(f"Index Number: {i}")
print(f"Name : {name}")
print(f"Chromebook : {chromebook}")
print(f"Time Of Checkout: {check_out} ")
print("\n\n")
i = i + 1
def delete_data(): # Deletes an element from the array
view_data()
new_data = []
with open(filename, "r") as f:
data = json.load(f)
data_length = len(data) - 1
print("Which index number would you like to delete?")
delete_option = input(f"Select a number 0-{data_length}: ")
i = 0
for entry in data:
if i == int(delete_option):
pass
i = + 1
else:
new_data.append(entry)
i = + 1
with open(filename, "w") as f:
json.dump(new_data, f, indent=4)
Here's the JSON file code:
[
{
"name": "Tyler",
"chromebook": "12123223",
"time&date": "Check Out Time: 13:33:22 May-11-2021"
},
{
"name": "Craig",
"chromebook": "41222224",
"time&date": "Check Out Time: 13:33:34 May-11-2021"
},
{
"name": "Bill",
"chromebook": "3235223",
"time&date": "Check Out Time: 13:33:46 May-11-2021"
}
]
For example, say the user wanted to remove the second index in the JSON array. Is there a better way to implement that in my Python script?
I'm still a fairly new and learning Python developer, and if there's any better solution I'm open to suggestions. If you need for info, I'll be active.
First question
However, whenever there's more than two elements and I enter anything higher than two, it doesn't delete anything. Even worse, when I enter the number one, it deletes everything but the zero index(whenever the array has more than two elements in it).
Inside delete_data() you have two lines reading i = + 1, which just assignes +1 (i.e., 1) to i. Thus, you're never increasing your index. You probably meant to write either i = i+1 or i += 1.
def delete_data(): # Deletes an element from the array
view_data()
new_data = []
with open(filename, "r") as f:
data = json.load(f)
data_length = len(data) - 1
print("Which index number would you like to delete?")
delete_option = input(f"Select a number 0-{data_length}: ")
i = 0
for entry in data:
if i == int(delete_option):
i += 1 # <-- here
else:
new_data.append(entry)
i += 1 # <-- and here
with open(filename, "w") as f:
json.dump(new_data, f, indent=4)
Second question: further improvements
Is there a better way to implement that in my Python script?
First, you can get rid of manually increasing i by using the builtin enumerate generator. Second, you could make your functions reusable by giving them parameters - where does the filename in your code example come from?
# view_data() should probably receive `filename` as a parameter
def view_data(filename: str): # Prints JSON Array to screen
with open(filename, "r") as f:
data = json.load(f)
# iterate over i and data simultaneously
# alternatively, you could just remove i
for i, item in enumerate(data):
name = item["name"]
chromebook = item["chromebook"]
check_out = item["time&date"]
print(f"Index Number: {i}")
print(f"Name : {name}")
print(f"Chromebook : {chromebook}")
print(f"Time Of Checkout: {check_out} ")
print("\n\n")
# not needed anymore: i = i + 1
# view_data() should probably receive `filename` as a parameter
def delete_data(filename: str): # Deletes an element from the array
view_data()
new_data = []
with open(filename, "r") as f:
data = json.load(f)
data_length = len(data) - 1
print("Which index number would you like to delete?")
delete_option = input(f"Select a number 0-{data_length}: ")
# iterate over i and data simultaneously
for i, entry in enumerate(data):
if i != int(delete_option):
new_data.append(entry)
with open(filename, "w") as f:
json.dump(new_data, f, indent=4)
Furthermore, you could replace that for-loop by a list comprehension, which some may deem more "pythonic":
new_data = [entry for i, entry in enumerate(data) if i != int(delete_option)]
There are easier ways to delete an element by index from a Python list.
Given li = ["a", "b", "c"], you can delete element 1 ("b") by index in (at least) the following ways:
li.pop(1) # pop takes an index (defaults to last) and removes and returns the element at that index
del li[1] # the del keyword will also remove an element from a list
So, here's some updated code:
def view_data(): # Prints JSON Array to screen
with open(filename, "r") as f:
data = json.load(f)
i = 0
for item in data:
name = item["name"]
chromebook = item["chromebook"]
check_out = item["time&date"]
print(f"Index Number: {i}")
print(f"Name : {name}")
print(f"Chromebook : {chromebook}")
print(f"Time Of Checkout: {check_out} ")
print("\n\n")
i = i + 1
def delete_data(): # Deletes an element from the array
view_data()
with open(filename, "r") as f:
data = json.load(f)
data_length = len(data) - 1
print("Which index number would you like to delete?")
delete_option = input(f"Select a number 0-{data_length}: ")
del data[int(delete_option)] # or data.pop(int(delete_option))
with open(filename, "w") as f:
json.dump(data, f, indent=4)
import json
data = json.loads(jsonString) #convert json string to object
delete_option = input(f"Select a number 0-{data_length}: ")
del data[int(delete_option)]
I have a stock file in the format of this:
12345678,Fridge,1,50
23456789,Car,2,50
34567890,TV,20,50
This is the code:
def main():
products = {}
#This is the file directory being made.
f = open('stockfile.txt')
#This is my file being opened.
for line in f:
# Need to strip to eliminate end of line character
line = line[:-1]
#This gets rid of the character which shows and end of line '\n'
row = line.split(',')
#The row is split by the comma
products[row[0]] = [row[1], row[2],row[3]]
#The products are equal to row 1 and row 2 and row 3. The GTIN is going to take the values of the product and price so GTIN 12345678 is going to correspond to Fridge and 1.
print(products)
total = 0
print('Id Description Total')
while True:
GTIN = input('Please input GTIN ')
if(GTIN not in products):
print('Sorry your code was invalid, try again:')
break
row = products[GTIN]
print(GTIN)
description = row[0]
value = row[1]
stock = row[2]
print(stock)
quantity = input('Please also input your quantity required: ')
row[2]= int(stock) - int(quantity)
products[row[2]] = row[2]
product_total= (int(quantity)*int(value))
New_Stock = GTIN + ',' + description + ',' + value + ',' + str(products[row[2]])
f = open('stockfile.txt','r')
lines = f.readlines()
f.close()
f = open("stockfile.txt","a")
for row in lines:
if((row + '\n') != (New_Stock + '\n')):
f.write(New_Stock)
f.close()
print('%20s%20s%20s' % (GTIN, description, product_total))
total = total + product_total
print('Total of the order is £%s' % total)
print(products)
main()
However, the code doesn't update the stock's. What it should do is get rid of the previous stock for the product given and then update it according to the quantity the user has just bought.
I haven't got to it yet but once the stock hits zero I need my code to then tell the user that we have run out of stock and need some new stock. Then there needs to be a message to the user to wait until we restock and then display the price of restocking as well.
If you have time please could you make this new bit of code as well but if not could you just explain how to update the stock and why my code isn't working, thank you.
When you seek to a given line and call write, in order to completely overwrite that line, without affecting other lines or inadvertently creating new lines, each line in your stock must have a fixed width. How do you ensure fixed width? By giving each field in the record a fixed width. Basically, you choose a maximum number of characters each field can have; here I'll assume 8 for all fields (though they must not all be the same), so your stock will be stored this way:
12344848, Fridge, 2, 50
13738389, TV, 5, 70
If you keep going this way, each line will have a maximum width which will enable you to seek to the start of the line and overwrite it completely. Try this code:
MAX_FIELD_LEN = 8
def main():
products = {}
product_location = {}
location = 0
# This is the file directory being made.
with open('stockfile.txt', 'r+') as f:
# This is my file being opened.
for line in f:
# keep track of each products location in file to overwrite with New_Stock
product_location[line.split(',')[0]] = location
location += len(line)
# Need to strip to eliminate end of line character
line = line[:-1]
# This gets rid of the character which shows and end of line '\n'
row = line.split(',')
# The row is split by the comma
products[row[0]] = [row[1], row[2], row[3]]
# The products are equal to row 1 and row 2 and row 3. The GTIN is going to take the values of the product and price so GTIN 12345678 is going to correspond to Fridge and 1.
print(products)
total = 0
while True:
GTIN = input('Please input GTIN: ')
# To terminate user input, they just need to press ENTER
if GTIN == "":
break
if (GTIN not in products):
print('Sorry your code was invalid, try again:')
break
row = products[GTIN]
description, value, stock = row
print('Stock data: ')
print('GTIN \t\tDesc. \t\tStock \t\tValue')
print(GTIN,'\t',description,'\t', stock, '\t', value)
quantity = input('Please also input your quantity required: ')
row[2] = str(int(stock) - int(quantity))
product_total = int(quantity) * int(value)
for i in range(len(row)):
row[i] = row[i].rjust(MAX_FIELD_LEN)
New_Stock = GTIN.rjust(MAX_FIELD_LEN) + ',' + ','.join(row) + '\n'
#print(New_Stock, len(New_Stock))
f.seek(product_location[GTIN])
f.write(New_Stock)
print('You bought: {0} {1} \nCost: {2}'.format(GTIN, description, product_total))
total = total + product_total
f.close()
print('Total of the order is £%s' % total)
main()
Ensure that each field in the TXT file is exactly 8 characters wide (not including the commas) when using this program. If you want to increase the field width, change the MAX_FIELD_LEN variable accordingly. Your TXT file should look like this:
In the first few lines you are loading the whole data file in memory :
for line in f:
products[row[0]] = [row[1], row[2],row[3]]
So then just update the data in memory, and have users enter a special command : "save" to write the whole list to your file.
You could also catch your application process KILL signal so if a user hits ctrl + c, you can ask him if he wants to save before quitting.
And maybe save a temporary copy of the list to a file every few seconds.
I suggest you use the shelve module for this, if your customers intend to run this program many times. Reading the whole file into memory and writing it anew as text will become inefficient as your stock grows. Shelve creates persistent files (3 files to be exact) on your PC for storing your data. Most importantly, shelve would give you the same dict interface you want, so that all you have to do is call shelve.open() on the file and you can begin accessing/updating your stock using GTINs as keys. Its very straightforward, just look at the python manuals. If you really want a text file, you could have your program, iterate through the shelve file containing your stock (same as for a dictionary) and write keys (GTIN) and their values (your stock quantity) to a text file you opened. This way, you have easy intuitive access to your records and also a readable format in your TXT file.
MAX_FIELD_LEN = 8
def main():
products = {}
product_location = {}
location = 0
# This is the file directory being made.
with open('stockfile.txt', 'r+') as f:
# This is my file being opened.
for line in f:
# keep track of each products location in file to overwrite with New_Stock
product_location[line.split(',')[0]] = location
location += len(line)
# Need to strip to eliminate end of line character
line = line[:-1]
# This gets rid of the character which shows and end of line '\n'
row = line.split(',')
# The row is split by the comma
products[row[0]] = [row[1], row[2], row[3]]
# The products are equal to row 1 and row 2 and row 3. The GTIN is going to take the values of the product and price so GTIN 12345678 is going to correspond to Fridge and 1.
print(products)
total = 0
while True:
GTIN = input('Please input GTIN: ')
# To terminate user input, they just need to press ENTER
if GTIN == "":
break
if (GTIN not in products):
print('Sorry your code was invalid, try again:')
break
row = products[GTIN]
description, value, stock = row
print('Stock data: ')
print('GTIN \t\tDesc. \t\tStock \t\tValue')
print(GTIN,'\t',description,'\t', stock, '\t', value)
quantity = input('Please also input your quantity required: ')
row[2] = str(int(stock) - int(quantity))
product_total = int(quantity) * int(value)
for i in range(len(row)):
row[i] = row[i].rjust(MAX_FIELD_LEN)
New_Stock = GTIN.rjust(MAX_FIELD_LEN) + ',' + ','.join(row) + '\n'
#print(New_Stock, len(New_Stock))
f.seek(product_location[GTIN])
f.write(New_Stock)
print('You bought: {0} {1} \nCost: {2}'.format(GTIN, description, product_total))
total = total + product_total
f.close()
print('Total of the order is £%s' % total)
main()
This was the text file:
12345678, Fridge, 1, 50
23456789, Car, 2, 50
34567890, TV, 20, 50
Does it make a difference I am doing this on a Mac desktop or that it is python 3.4.3?
The suggestion above using shelve sounds like a good idea, but if you want to keep your file as is but only update the records that are changed (instead of rewriting the whole file each time) using (most) of your code, this seems to work.
def main():
products = {}
product_location = {}
location = 0
# This is the file directory being made.
with open('stockfile.txt', 'r+') as f:
# This is my file being opened.
for line in f:
# keep track of each products location in file to overwrite with New_Stock
product_location[line.split(',')[0]] = location
location += len(line)
# Need to strip to eliminate end of line character
line = line[:-1]
# The row is split by the comma
row = line.split(',')
products[row[0]] = [row[1], row[2], row[3]]
"""
The products are equal to row 1 and row 2 and row 3. The GTIN is going to take the values of the product and
price so GTIN 12345678 is going to correspond to Fridge and 1.
"""
print(sorted(products.items()))
total = 0
while True:
GTIN = input('\nPlease input GTIN or press [Enter] to quit:\n')
# To terminate user input, they just need to press ENTER
if GTIN == "":
break
if (GTIN not in products):
# Let the user continue with order after mistake in GTIN input
print('Sorry your code was invalid, try again:')
continue
row = products[GTIN]
print('GTIN:', GTIN)
description = row[0]
value = row[1]
stock = row[2]
stock_length = len(row[2])
backorder = 0
print('In Stock:', stock)
quantity = input('Please also input your quantity required:\n')
if int(quantity) > int(stock):
row[2] = 0
backorder = int(quantity) - int(stock)
# TO DO
Backordered_Stock = GTIN + ',' + description + ',' + value + ',' + str(backorder) + '\n'
else:
row[2] = int(stock) - int(quantity)
products[row[2]] = row[2]
product_total = (int(quantity) * int(value))
New_Stock = GTIN + ',' + description + ',' + value + ',' + str(products[row[2]]).rjust(stock_length) + '\n'
f.seek(product_location[GTIN])
f.write(New_Stock)
print('Ordered - {0:>6} GTIN: {1:>10} Desc: {2:<20} at £{3:>6} Total value: £{4:>6} On backorder: {5:>4}'.
format(int(quantity), GTIN, description, int(value), product_total, backorder))
total = total + product_total
print('Total of the order is £%s' % total)
main()
I am trying to create 5 lists out of 1 data file, the error I keep getting states that "airlines is not defined", yet it is the first thing I define in the function, how is this possible? What should I do to correctly create a list of airlines, arrival times, departure times, prices, and flight number?
USAir,1269,6:15,10:57,210
Delta,5138,16:20,22:10,212
UNITED,6001,14:12,20:50,217
Delta,5054,12:30,20:22,227
UNITED,5949,9:30,14:43,264
JetBlue,1075,17:00,20:06,280
Delta,1263,6:00,11:30,282
Delta,3824,9:00,14:45,282
USAir,1865,16:55,21:33,300
USAir,3289,18:55,23:41,300
USAir,1053,8:00,13:02,300
USAir,2689,12:55,18:09,300
USAir,3973,9:25,14:00,302
USAir,3267,11:30,16:13,302
USAir,3609,13:25,18:28,302
USAir,3863,15:35,20:54,302
USAir,3826,17:45,23:19,302
USAir,1927,7:00,12:53,302
Delta,3601,12:00,17:29,307
Delta,4268,7:15,12:46,307
UNITED,4676,6:00,10:45,321
UNITED,4103,11:00,16:16,321
USAir,3139,11:51,16:29,332
JetBlue,475,7:30,10:42,340
USAir,3267,11:30,18:15,367
UNITED,2869,16:55,21:33,406
UNITED,2865,6:15,10:57,406
UNITED,2729,8:00,13:02,406
UNITED,2645,7:00,12:53,445
and the code I am using is
def getFlights():
airlines = []
flightNums = []
depTimes = []
arriveTimes = []
prices = []
fname = input("Enter name of data file: ")
infile = open(fname, 'r')
line = infile.readline()
line = line.strip()
while line != "":
line = line.strip()
airline, flightNum, depTime, arriveTime, price = line.split(',')
airlines.append(airline)
flightNums.append(flightNum)
depTimes.append(depTime)
arriveTimes.append(arriveTime)
prices.append(price)
line = infile.readline()
line = line.strip()
infile.close()
return airlines, flightNums, depTimes, arriveTimes, prices
getFlights()
print(airlines, flightNums, depTimes, arriveTimes, prices)
Local variables inside a function are not accessible outside of the function call. If you want to use the returned values of getFlights you must assign them to variables in the calling context.
(airlines, flightNums, depTimes, arriveTimes, prices) = getFlights()
print(airlines, flightNums, depTimes, arriveTimes, prices)
What b4hand has said is correct, however, the Pythonic way of doing this is using csv.reader and a with statement, eg:
import csv
filename = input('Enter filename: ')
with open(filename, 'rb') as fin:
csvin = csv.reader(fin)
airlines, flightNums, depTimes, arriveTimes, prices = zip(*csvin)