Struggling with JSON

We’re doing a school musical, I’m on the enterprise team, details unimportant. I agreed to make a way to keep track of ticket sales, I have this simple code but my problem is I need a way to save the data I record with it. From what I found the best way to do that is with JSON (if you can think of a better way that’ll help too :slight_smile:) but I’m really struggling to get my head around how it works and how to incorporate it into my code, can anyone help me out?

what i have so far:

names = [["sample name one"],["sample name two"],["sample name three"]] #list of names, sample name to avoid issue with ticket 0
z = False #unused varible, just to create infinite loop
price = 10


while z == False: #infinite loop
    run = input("what would you like to do? ") #user chooses action


    if run == "sell": #sell tickets
        
        name = input("name? ") #name of buyer
        night = int(input("night? "))-1
        names[night].append(name)
        print("ticket number:",len(names[night])) #ticket number is just where you are on the list


    if run == "check": #shows list of who has what ticket and how much money there should be
        
        for j in range (3):
            
            print("night:",j+1)
            print("name\tticket #")
            for i in range (len(names[j])):
                print(names[j][i],"\t",i)
                
            print("€",(len(names[j])-1)*price) #money from one night
            print()
            
    
        print("€",(len(names[0]+names[1]+names[2])-3)*price," total") #money from all nights

    print()

planning on adding stuff like a search system through the names but I figured saving would be more important to do first, apologies for my comments I don’t know fancy coding lingo I tried my best haha, still a beginner.

Saving:

from json import dump

data = ...

with open("/path/to/file.json", "w", encoding="utf_8") as file:
    dump(data, file)

Restoring:

from json import load

with open("/path/to/file.json") as file:
    data = load(file)

So, like this, note that you’ll still need to write [["sample name one"], ["sample name two"], ["sample name three"]] to the file manually:

from json import dump, load

price = 10
# load saved file
with open("/path/to/file.json") as file:
    names = load(file)

while True:
    run = input("what would you like to do? ")
    if run == "sell":
        name = input("name? ")
        night = int(input("night? ")) - 1
        names[night].append(name)
        print("ticket number:", len(names[night]))
        # save file
        with open("/path/to/file.json", "w", encoding="utf_8") as file:
            dump(names, file)
    elif run == "check":
        for j in range(3):
            print("night:", j + 1)
            print("name\tticket #")
            for i in range(len(names[j])):
                print(names[j][i], "\t", i)

            print("€", (len(names[j]) - 1) * price)
            print()

        print("€", (len(names[0] + names[1] + names[2]) - 3)
              * price, " total")

    print()

You don’t need to add any fake data to the file - just have [] in there on its own and json.loads will load it as an empty list.
I would also add indent=1 to the call to json.dump:

dump(names, file, indent=1)

to make it easier to inspect the file output.

The code assumes the length of the list is 3, so you can’t do that.

OK So the seed data should be:

[
    [],
    [],
    []
]

To initialize it to 3 empty lists.
Personally I think a dict would be a better data structure with the key being the night and the value being the list of names:

        name = input("name? ")
        night = int(input("night? ")) - 1
        names.setdefault(night, list).append(name)

would be the minimal change required. The seed data in the JSON file would just be {} for that.

In any case, data validation should also be handled to ensure that night is a valid value.

apologies for taking so long to respond I’ve been sick last few days and am just coming back to this. from the best of my understanding of the responses I’ve come up with this, it seems to be working fine but id love a second opinion on it. I still have to improve the formatting though. thank you everyone for the help!

from json import dump, load

price = 10

#load
with open("C:/Users/anaki/OneDrive/TY musical ticket sales code/ticketSales.json") as file:
    names = load(file)

while True: 
    run = input("what would you like to do? ") #user chooses action


    if run == "sell": #sell tickets
        
        name = input("name? ") #name of buyer
        night = int(input("night? "))-1
        names[night].append(name)
        print("ticket number:",len(names[night])) #ticket number is just where you are on the list


        with open("C:/Users/anaki/OneDrive/TY musical ticket sales code/ticketSales.json", "w", encoding="utf_8") as file: #save
            dump(names, file, indent=1)


    if run == "check": #shows list of who has what ticket and how much money there should be
        
        for j in range (3):
            
            print("night:",j+1)
            print("name\tticket #")
            for i in range (len(names[j])):
                print(names[j][i],"\t",i)
                
            print("€",(len(names[j])-1)*price) #money from one night
            print()
            
    
        print("€",(len(names[0]+names[1]+names[2])-3)*price," total") #money from all nights

    print()

I don’t fully understand dictionaries or their function, never actually used one. But I don’t see the benefit of using one, do the names not automatically have a number assigned to them because of their place in the list anyway? why also store that data?

Dictionaries are key/value pairs. The enable you to look up a value by its key. For example:

>>> d = {'Monday': ['Joe', 'Jane'], 'Tuesday': ['David', 'Daisy']}
>>> d['Monday']
['Joe', 'Jane']

We can also iterate on the dictionary in a for loop:

>>> for day, people in d.items():
...     print(f'{day}: {", ".join(people)}')
...
Monday: Joe, Jane
Tuesday: David, Daisy

I would actually rewrite your check section like this:

# if names is a list:
for night in names:
    print('\t'.join(night))
    print(f'€{len(night) * price}\n')   # The length of the list is the number of tickets you've sold so subtracting 1 means you're reporting less money by 1 ticket per night.
print(f'€{sum(map(len, names)) * price} total.')

# if names is a dict:
for night in names.values():
# The rest is the same except the total print:
print(f'€{sum(map(len, names.values())) * price} total.')

So I think I introduced something that might be unfamiliar - the map function. map takes 2 parameters - a function to call and an iterable to iterate over it. What it does is that it calls the function on every value in the iterable.

>>> sum(map(len, d.values()))
4

Effectively it looks like this:

def map(func, iter):
    rc = []
    for i in iter:
        rc.append(func(i))
    return rc

(It does not return a list - it’s a generator but I don’t want to go into that right now - too many new things and all that. It’s an iterable and you can turn it into a list by running list(map(f, i)) but it will do what you need here without you necessarily understanding the internals.)

I hope this helps you understand dictionaries a bit more.

i think i get dictionaries a little more now :). my dad fixed up the formatting for me and the first day of ticket sales went pretty well, changed it to a dictionary too.
my issue now is that the tickets are €15 but if you buy 2 it’s €25, i was just recording the money loss by putting it direcly into the code every time someone bought multiple tickets, How would i add that extra variable into the json file?

from json import dump, load
import os

price = 15  #ticket price

PATH = "C:/Users/anaki/OneDrive/TY musical/ticketSales.json"

try:
   with open( PATH, "r") as file:
       names = load(file)
except:
   names = [
         [ { "name": "Sample Name 1", "email": "name@sample1", "paid": "yes" } ],
         [ { "name": "Sample Name 2", "email": "name@sample1", "paid": "yes" } ],
         [ { "name": "Sample Name 3", "email": "name@sample1", "paid": "yes" } ]
         ]
   cashLoss = 0

while True:
    run = input("what would you like to do? ") #user chooses action

    #sell tickets
    if run == "sell":

        name = input("name? ") #name of buyer
        email = input("email? ") #name of buyer
        night = int(input("night? "))-1 #night of the musical, 3rd dec = night1 ...etc
        paid = input("Paid? ")

        entry = { "name": name, "email": email, "paid": paid }
        names[night].append( entry ) #adds name to list
        print("ticket number:",len(names[night])) #ticket number is just where you are on the list

        try:
           os.rename(PATH, PATH + ".backup" )
        except:
           pass
        with open(PATH, "w", encoding="utf-8") as file:
            dump(names, file, indent=1) #saves to json file


#shows list of who has what ticket and how much money there should be
    if run == "check":

        for j in range (3):

            print("night:",j+1)
            print(f"{'name':25}{'email':25}{'ticket num':<25}")
            print(f"{'====':25}{'=====':25}{'==========':<25}")

            for i in range (len(names[j])):
                #print(names[j][i]["name"],"\t",i)
                print( f'{names[j][i]["name"]:25}{names[j][i]["email"]:25}{i:<25}')

#                      names[j][i]["name"],"\t",i)

            print("€",(len(names[j])-1)*price) #money from one night
            print()


        print("€",((len(names[0]+names[1]+names[2])-3)*price)-30," total") #money from all nights

#remove for 2 for €25 deal
    if run == "remove":
        cashLoss += 5

    print() #blank line for neater output

2 Likes

You could change paid from a yes/no field to a numeric and record the amount. So if someone bought multiple tickets the first would be 15 and all subsequent tickets would be 10 and then just get the sum for that:

sum(ticket.get('paid', 0) for ticket in names[day])

That would store whether the person paid (if they didn’t it would be 0) and how much if they did.

that actually a great idea I’ll implement that. Thank you!! :smiley:

1 Like