Generating Random Combinations of Dictionary Key and Value Sets - python

I am creating a program to sort through a music library file. I have this one particular function, generateRandomPlaylist(musicLibDictionary), that I am stuck on.
The function needs to randomly pick a key from the dictionary and randomly assign one of the values to the key. For instance the artists in the dictionary include The Who, Adele and The Beatles. The respective albums include Tommy; 19, 21, 25; Abbey Road, Let It Be. I need the program to randomly pick one of the keys (the artists) and then randomly pick one of the key's values. The randomly generated playlist needs to have all three artists, not repeats, but different albums from the artist. The way I have it set up, the output doesn't necessarily use all three artists. Sample output should look like:
Here is your random playlist:
- 25 by Adele
- Abbey Road by The Beatles
- Tommy by The Who
Each time the function is called, the playlist should be different. Like I said, right now the function doesn't run properly, and I also get a printing error saying I cannot concatenate a list and a string, so I don't know where I am going wrong.
The code for the program is below:
# importing pickle
import pickle
import random
# declaration of functions
def displayMenu():
print("Welcome to Your Music Library")
print("Options:")
print("\t1) Display Library")
print("\t2) Display all artists")
print("\t3) Add an album")
print("\t4) Delete an album")
print("\t5) Delete an artist")
print("\t6) Search Library")
print("\t7) Generate a random playlist")
print("\t8) Make your own playlist")
print("\t9) Exit")
def displayLibrary(musicLibDictionary):
for key in musicLibDictionary:
print("Artist: " + key)
print("Albums: ")
for album in musicLibDictionary[key]:
print("\t- " + album)
def displayArtists(musicLibDictionary):
print("Displaying all artists:")
for key in musicLibDictionary:
print(" - " + key)
def addAlbum(musicLibDictionary):
artistName = input("Please enter the name of the artist you would like to add: ")
albumName = input("Please enter the name of the album you would like to add: ")
if artistName in musicLibDictionary.keys():
musicLibDictionary[artistName].append(albumName)
else:
musicLibDictionary[artistName] = [albumName]
def deleteAlbum(musicLibDictionary):
artist = input("Enter artist: ")
albumToBeDeleted = input("Enter album: ")
if artist in musicLibDictionary.keys():
if albumToBeDeleted in musicLibDictionary[artist]:
musicLibDictionary[artist].remove(albumToBeDeleted)
return True
else:
return False
else:
return False
def deleteArtist(musicLibDct):
artistToBeDeleted = input("Enter artist to delete: ")
if artistToBeDeleted in musicLibDct.keys():
del musicLibDct[artistToBeDeleted]
return True
else:
return False
def searchLibrary(musicLibDictionary):
searchTerm = input("Please enter a search term: ")
searchTerm = searchTerm.lower()
print("Artists containing" + searchTerm)
for key in musicLibDictionary.keys():
if searchTerm.lower() in key.lower():
print("\t - ", end="")
print(key)
print("Albums containing" + searchTerm)
for album in musicLibDictionary[key]:
print("\t- " + album)
for key in musicLibDictionary.keys():
for album in musicLibDictionary[key]:
if searchTerm in album.lower():
print("\t - ", end="")
print(album)
def generateRandomPlaylist(musicLibDictionary):
print("Here is your random playlist:")
for artist in musicLibDictionary.keys():
artistSelection = random.choice(list(musicLibDictionary.keys()))
albumSelection = random.choice(list(musicLibDictionary.values()))
print("\t-" + albumSelection + "by" + artistSelection)
def loadLibrary(libraryFileName):
fileIn = open(libraryFileName, "rb")
val = pickle.load(fileIn)
val = dict(val)
return val
def saveLibrary(libraryFileName, musicLibDictionary):
fileIn = open(libraryFileName, "wb")
pickle.dump(musicLibDictionary, fileIn)
def main():
musicLib = loadLibrary("musicLibrary.dat")
userChoice = ""
while (userChoice != 7):
displayMenu()
userChoice = int(input("> "))
if userChoice == 1:
displayLibrary(musicLib)
elif userChoice == 2:
displayArtists(musicLib)
elif userChoice == 3:
addAlbum(musicLib)
elif userChoice == 4:
deleteAlbum(musicLib)
elif userChoice == 5:
deleteArtist(musicLib)
elif userChoice == 6:
searchLibrary(musicLib)
elif userChoice == 7:
generateRandomPlaylist(musicLib)
elif userChoice == 8:
saveLibrary("musicLibrary.dat", musicLib)
# Call main
main()

Here's one way to do it, if I understand correctly that you want each artist exactly once in a random order and a random album from each artist:
def generateRandomPlaylist(musicLibDictionary):
print("Here is your random playlist:")
artists = list(musicLibDictionary.keys())
random.shuffle(artists) # Perform an in-place shuffle
for artistSelection in artists:
albumSelection = random.choice(list(musicLibDictionary[artistSelection]))
print("\t-" + albumSelection + "by" + artistSelection)
We know we want all the artists exactly once, but in a random order. Since the keys of the dictionary are the artists, then we can just perform a random.shuffle on the keys to get a random ordering of the artists. Then we have to look into each artist's albums (musicLibDictionary[artist]) and make a random.choice to pick out one album at random.
What your code is doing, on a line by line basis, is the following:
def generateRandomPlaylist(musicLibDictionary):
print("Here is your random playlist:")
for artist in musicLibDictionary.keys(): # For each artist
artistSelection = random.choice(list(musicLibDictionary.keys())) # Choose one artist randomly (not necessarily the one in your for loop)
albumSelection = random.choice(list(musicLibDictionary.values())) # Choose a random album list (musicLibDictionary.values() returns a list of lists, so you're just choosing a random discography (list) from a random artist)
print("\t-" + albumSelection + "by" + artistSelection) # Because this is not indented into your loop, you're likely only getting the last one chosen
The reason you were getting issues appending a string and a list (not allowed in python directly, you must cast the list to a string first) is that your albumSelection variable was a full list of the albums from a random artist, not necessarily even the artist in the artistSelection variable. Your musicLibDictionary.values() was returning something like [['Tommy'], ['19', '21', '25'], ['Abbey Road', 'Let It Be']]. random.choice picks a random value out of the list provided to it so given a list like [[1,2,3], [4,5,6]] it could pick out [1,2,3].

Related

Stuck on what to do next in this program and the question is this. Create a Python program that finds a city and state from a given zip code?

def main():
print("\nZIPCODE LOOKUP PROGRAM")
zlist,clist,slist=loadLists()
userInput = searchLoop(zlist,clist,slist)
def loadLists():
# create a list
try:
zlist = []
clist = []
slist = []
# open the file
zipFile = open("zipcodes.txt", "r")
for nextline in zipFile:
parsedlist = nextline.split(",")
zlist.append(parsedlist[0])
clist.append(parsedlist[1])
slist.append(parsedlist[2])
zipFile.close()
except IOError:
print("\nError - zipcodes.txt does not exist")
return zlist,clist,slist
def searchLoop(zlist,clist,slist):
# search the zip code the user entered
userInput = input("\nEnter a zip code to find (Press Enter key alone to stop): ")
while userInput != "":
if userInput in zlist:
where = zlist.index(userInput)
print("\n" + "\tThe city is " + city + "and the state is" + state + ".")
city = clist.index()
state = slist.index()
else:
print("\n" + "\tZipcode does not exist")
userInput = input("\nEnter a zip code to find (Press Enter key alone to stop): ")
return userInput
def findZipcode():
# find the zip code, city, and state
# what do I do about this
def printSearchResults():
#print the result
print("\n" + "\tThe city is " + city + "and the state is" + state + ".")
This is what it is suppose to look like
Enter a zip code to find (Press Enter key alone to stop): 90401
The city is SANTA MONICA and the state is CA.
You have parallel arrays (i.e., related information shares the same index within the arrays) containing the zip code, state and city, and you are given a zip code to retrieve information for.
The idea then would be to find the index of the item in zlist that matches the given zip code, and use that index to extract the city and state from the other two arrays.
For example (Python-looking pseudo-code):
define getInfo(zlist, clist, slist, zipcode):
for i = 0 to len(zlist) - 1 inclusive:
if zlist[i] = zipcode:
return clist[i] and slist[i]
return nothing and nothing
I cleaned up your code and I think the best approach is to use just a loop and a dictionary with keys to store the results, not lists.
def main():
"""Main function that triggers all other functions"""
print("\nZIPCODE LOOKUP PROGRAM")
my_list = load_lists()
search_loop(my_list)
def load_lists():
"""Open file, create a list with the data parsed as a list of dicts"""
my_list = []
with open("zipcodes.txt", "r") as zip_file:
for nextline in zip_file:
parsedlist = nextline.split(",")
my_list.append({"Zip":parsedlist[0], "City":parsedlist[1], "State":parsedlist[2]})
return my_list
def search_loop(my_list):
"""Search the zip code the user entered using a loop.
Then print that dictionary formatted for readability"""
user_input = input("\nEnter a zip code to find (Press Enter key alone to stop): ")
while user_input != "":
for item in my_list:
if str(user_input) == item['Zip']:
print(f"\n\tThe city is {item['City']} and the state is {item['State']}.")
break # No need to check other results since we found it
else:
print("\n\tZipcode does not exist")
userInput = input("\nEnter a zip code to find (Press Enter key alone to stop): ")
return
main() # Run the program
Result:
ZIPCODE LOOKUP PROGRAM
Enter a zip code to find (Press Enter key alone to stop): 1234
The city is TestCity and the state is Testing.
Enter a zip code to find (Press Enter key alone to stop): 4567
The city is MadeUpCity and the state is MadeUp.
Things to have in mind:
Use with to read files such as .txt, it will open and close them automatically and you'll avoid most of the errors that way. Read about it.
You can use docstrings (triple quoted string) as first things on what a function should do, to avoid the need of multiple comments everywhere.
And please don't use CamelCase in python except for classes. This
is explained in PEP8
f-strings makes prints prettier. f:"this is {city} and this is {state}." instead of "this is " + city + " and this is " + state + "."

how do i fix my problem with my tuple in python

i have a problem in python i have been told that is is to do with the tuple in the code the idle gives me this error after login
Traceback (most recent call last):
artist, song = choice.split()
ValueError: need more than 1 value to unpack
this is my full code
import random
import time
x = 0
print("welcome to music game please login below")
AuthUsers = {"drew":"pw","masif":"pw","ishy":"pw"}
#authentication
PWLoop = True
while PWLoop:
userName = input("what is your username?")
#asking for password
password = (input("what is the password?"))
if userName in AuthUsers:
if AuthUsers.get(userName) == password:
print("you are allowed to play")
PWLoop = False
else:
print("invalid password")
else:
print("invalid username")
#GAME
#SETTING SCORE VARIBLE
score = 0
#READING SONGS
read = open("SONGS.txt", "r")
songs = read.readline()
songlist = []
for i in range(len(songs)):
songlist.append(songs[i].strip())
while x == 0:
#RANDOMLY CHOSING A SONG
choice = random.choice(songlist)
artist, song = choice.split()
#SPLITTING INTO FIRST WORDS
songs = song.split()
letters = [word[0] for word in songs]
#LOOp
for x in range(0,2):
print(artist, "".join(letters))
guess= str(input(Fore.RED + "guess the song"))
if guess == song:
if x == 0:
score = score + 2
break
if x == 1:
score = score + 1
break
#printing score
Issue is due to these two lines:
choice = random.choice(songlist)
# choice will be single item from songlist chosen randomly.
artist, song = choice.split() # trying to unpack list of 2 item
# choice.split() -> it will split that item by space
# So choice must be a string with exact one `space`
# i.e every entry in songlist must be string with exact one `space`
As the format of your file https://pastebin.com/DNLSGPzd
And To fix the issue just split by ,
Updated code:
artist, song = choice.split(',')
Change artist, song = choice.split() to:
song, artist = choice.split(',')
this will fix your problem.
according to the data that you given, you should split with ,.\

how to fix "guess the song game" python

I am trying to create this game for guessing songs however it will not allow me to have over one song even after i change the range I also would like to know how to add the code to do scores for guessing the write song.
import random
for x in range(0,1):
randNum = int(random.randint(0,1))
song = open("Songs.txt", "r")
songname = str(song.readlines()[randNum])
print(songname[0])
song.close()
artist = open("Artists.txt", "r")
artistname = artist.readlines()[randNum]
print(artistname[0])
artist.close()
y = 0
songGuess = input("What is the song called?")
while(y<=2):
if songGuess == songname:
print("Answer correct!")
break
else:
y = y + 1
songguess = input("Incorrect! Try again:")
if y == 2:
print("GAME OVER")
break
You need to change your random.randint range to random.randint(0,len(song.readlines())-1), so that you are chosing a random index from all listed songs,and do the same thing with artists.
A better approach is to choose a random element from the list using random.choice .
Your for range range(0,1) will cause your loop to run just once, update the range accordingly
You can use with keyword to automatically close the file instead of explicitly closing it.
So according to above changes, the fixed code might look like
import random
num_attempts = 10
#Run loop for num_attempts times
for x in range(0, num_attempts):
songname = ''
artistname = ''
#Use with to open file
with open('Songs.txt') as song:
#Read all songs into a list
songs = song.readlines()
#Choose a random song
songname = random.choice(songs)
print(songname)
with open('Artists.txt') as artist:
# Read all artists into a list
artists = artist.readlines()
# Choose a random artist
artistname = random.choice(artists)
print(artistname)
y = 0
#Play your game
songGuess = input("What is the song called?")
while(y<=2):
if songGuess == songname:
print("Answer correct!")
break
else:
y = y + 1
songguess = input("Incorrect! Try again:")
if y == 2:
print("GAME OVER")
break

Updating a list in a Python class

I want to update a list to include new items added by a user. There are a few conditions such as the code must be 7 digits long. If the code already exists, the system will notify the user. If the user tries to add another copy of 'up' with a different code, the system will not allow it. It will make them try again as the code must be the same. Eventually I will include a video number, so if there are two copies of 'up' they will have two different video numbers but the same video code.
Can someone show me why the following code is not working for me?
all_movies = []
class Movie(object):
movie_list = []
def __init__(self, code, title, director):
self.code = code
self.title = title
self.director = director
Movie.movie_list.append(self)
#staticmethod
def add_movie():
mv_code = input("Code of movie: ")
movie_code = int(mv_code)
movie_title = input("Name of movie: ")
movie_director = input("Director: ")
if len(mv_code) == 7:
all_movies.append(Movie(movie_code, movie_title, movie_director))
print("movie added to database")
else:
print("the code must be 7 digits long, add movie again.")
def check_validity(movie_code, all_movies):
if movie_code in all_movies:
return True
else:
return False
if check_validity(movie_code, all_movies):
all_movies[all_movies] += Movie
print()
print("updated")
else:
all_movies[movie_code] = [movie_code, movie_title, movie_director]
def main():
movie1 = Movie(1122334, 'Up', 'Director')
movie2 = Movie(1231235, 'Taxi Driver', 'Film-maker')
This is the error message that I am receiving:
all_movies[movie_code] = [movie_code, movie_title, movie_director]
IndexError: list assignment index out of range
First of all this structure is not suitable for your desires.
About the error you got, I should say that you have to define you all movies as a dictionary not a list (because of the that you want to use for each movie).
try this:
all_movies = {}
in you add_movie method:
#staticmethod
def add_movie():
mv_code = input("Code of movie: ")
movie_code = int(mv_code)
movie_title = input("Name of movie: ")
movie_director = input("Director: ")
if len(mv_code) == 7:
if movie_code in all_movies.keys():
print("the movie already exists")
# what do you want to happen here ?
else:
all_movies[movie_code] = (movie_code, movie_title, movie_director)
print("movie added to database")
else:
print("the code must be 7 digits long, add movie again.")
This will add a movie to the all_movies and you don't need the rest your code, and i didn't understand the usage of init and movie_list.
Try this, then tell me what happens if the code already exists in the movies, I will update my answer for you.
UPDATE:
According to your desires in comment the method will updated to something like this:
#staticmethod
def add_movie():
mv_code = input("Code of movie: ")
movie_code = int(mv_code)
movie_title = input("Name of movie: ")
movie_director = input("Director: ")
if len(mv_code) == 7:
if movie_code in all_movies.keys():
print("the movie is already exists, adding it with another video number")
# all_movies[movie_code][-1] is the last video with an existing key
# all_movies[movie_code][-1][-1] last video number generated
new_video_number = all_movies[movie_code][-1][-1] + 1
all_movies[movie_code].append([movie_title, movie_director, new_video_number]) # adding it with new video number
print("movie added to database with new video number")
else:
all_movies[movie_code] = []
all_movies[movie_code].append([movie_title, movie_director, 1]) # 1 is the first movie added(video number)
print("movie added to database")
else:
print("the code must be 7 digits long, add movie again.")
This will returns all_movies like this:
{
'1231235':[
['Taxi Driver', 'Film-maker', 1]
]
'1122334':[
['Up', 'Director',1],
['Up', 'Director',2],
['Up', 'Director',3],
]
'1122333':[
['Another movie', 'Another Director',1],
['Another movie', 'Another Director',2],
]
}
The last element of inner list are the video_numbers that generated automatically by system.

How to pull information from classes through raw input

I'm well into development of a text-based RPG. Right now, my store system is very long and convoluted, in that there is a lot repeated code. The idea I currently have going on is that I have a list of items available to sell, and based off raw input from the user, it will related those items to if / else statements, assuming I have the proper item and player classes made, i.e.:
store = ['sword', 'bow', 'health potion']
while True:
inp = raw_input("Type the name of the item you want to buy: ")
lst = [x for x in store if x.startswith(inp)
if len(lst) == 0:
print "No such item."
continue
elif len(lst) == 1:
item = lst[0]
break
else:
print "Which of the following items did you mean?: {0}".format(lst)
continue
if item == 'sword':
user.inventory['Weapons'].append(sword.name)
user.strength += sword.strength
user.inventory['Gold'] -= sword.cost
elif item == 'bow'
#Buy item
#Rest of items follow this if statement based off the result of item.
As you can see, I'm using the result of the 'item' variable to determine a line of if / elif / else statements for each item, and what happens if that item name is equal to the variable 'item'.
Instead, I want the player to be able to type in the item name, and then for that raw input to be translated to class names. In other words, if I typed in 'sword', I want Python to pull the information from the 'sword' object class, and apply those values to the player. For example, a weapon's damage is transferred to the player's skill. If a sword does 5 strength damage, the player's strength will be raised by 5. How can I get python to add the values of one class to another without a shit ton of if / else statements?
If you have all your game item classes names in a single place (for example, a module), you can use Python's getattr to retrieve the class itself having its string.
SO, for example, let's suppose you have a items.py file that does something like:
from weapons import Sword, Bow, Axe, MachinneGun
from medicine import HealthPotion, MaxHealthPotion, Poison, Antidote
(or just define those classes right there in the items module)
You can there proceed to do:
import items
...
inp = raw_input("Type the name of the item you want to buy: ")
...
item_class = getattr(items, inp)
user.inventory.append(item_class.__name__)
if hasattr(item_class, strength):
user.strength += item_class.strength
and so on.
You can also simply create a dictionary:
from items import Sword, Bow, HealthPotion
store = {"sword: Sword, "bow": Bow, "health potion": HealthPotion}
...
item_class = store[inp]
...
Note that the text is quoted- it is text data, and the unquoted values are the actual Python classes- which have all the attributes and such.
Thanks to jsbueno, my code now works. Here is my official fix using his dictionary method:
from objects import ironsword
class player(object):
def __init__(self, strength):
self.strength = strength
self.inventory = []
user = player(10)
store = {'iron longsword': ironsword}
while True:
inp = raw_input("Type the name of the item you want to buy: ")
lst = [x for x in store if x.startswith(inp)]
if len(lst) == 0:
print "No such item."
continue
elif len(lst) == 1:
item = lst[0]
break
else:
print "Which of the following items did you mean?: {0}".format(lst)
continue
item_class = store[item]
user.inventory.append(item_class.name)
user.strength += item_class.strength
print user.inventory
print user.strength
Typing even 'iron' into the raw input will pull the correct item. When printing user.inventory, it returns the correct item name, e.g. ['iron longsword'], and when printing the user strength variable, it prints the repsective amount.

Categories