class MySong:
_songTitle = "Song Title"
_artistName = "Artist Name"
_likeIndicator = -1
def setTitleAndArtist(self, songTitle, artistName):
self._songTitle = songTitle
self._artistName = artistName
def setLike(self, likeIndicator):
self._likeIndicator = likeIndicator
def undoSetLike(self, songTitle):
Null
def getTitle(self):
return self._songTitle
def getArtist(self):
return self._artistName
def getLikeIndicator(self):
return self._likeIndicator
class MyPlaylist:
_mySongs = []
def add(self, song):
self._mySongs.append(song)
def showTitles(self):
index = 0
titlesList = []
while index != len(self._mySongs):
titlesList.append(self._mySongs[index].getTitle())
index = index + 1
return titlesList
def remove(self):
remindex = 0
while remindex != len(self._mySongs):
if (self._mySongs[index].getTitle()) == remChoice :
return("Song FOUND debug!")
self._mySongs.remove(index)
else:
remindex = remindex + 1
return("Song NOT FOUND debug!")
def getMySong(self):
Null
There is a list of song objects inside of _mySongs = []. I'm trying to remove one, based on the title variable of that object.
In a separate (unshown) part of the program, the user is asked to enter the title of the song they want removed as a string. This is saved as remChoice.
I'm not entirely sure how to remove the song based on the title.
I've tried for a while to get it going, obviously we find the index of the song in the list by matching it to the title (by calling the getTitle method), then removing that index when it's found.
This isn't working. Where am I going wrong?
If you want to delete an item from a list knowing it's index use:
del xs[i]
Where i is the index. (e.g: Your song's index based on your search).
list.remove() is used for removing a matching element form the list not the "ith" item.
You might also find that a list is not a suitable data structure here? Perhaps you could try storing key/value pairs in a dict. e.g:
my_songs = {}
my_aongs["My Song Title"] = MySong(title, description, length)
You can later delete songs via their keys:
del my_songs["My Song Title"]
where titles are your keys. This saves you from doing O(n) searching.
Update:
Your .remove() method should look more like the following:
def remove(self, title):
for i, song in enumerate(self._mySongs):
if song.getTitle() == title:
del self._mySongs[i]
return
print("Song not found!")
Here we're using list's iteration protocol by using a for x in xs: rather than using a while loop and doing manual bookkeeping. The builtin function enumerate() is also used to give us an index into the list we're iterating over (i.e: it's position in the sequence).
try
self._mySongs.remove(title)
That should work.
(Or from another object: replace self by whatever your object name is)
Related
I'm supposed to make a function with a list and a title as a string, and then return the item from
the list, based on the title.
def find_appointment(lst, title = ""):
if title in lst:
funn = lst.find(title)
return funn
else:
print("No result")
appointments = ["Zoo: 11.03.22", "Shopping: 13.08.22", "Christmas: 24.12.22", "Funeral: 25.12.22"]
find_appointment(appointments, "Zoo")
I hoped to get "Zoo: 11.03.22", but instead got "No result"
The list here is just a random one I made up. In the actual list I won't know the positions of
the items.
Here is my solution:
def find_appointment(lst, title = ""):
for i in lst:
if title in i:
return index
else:
print("No result")
appointments = ["Zoo: 11.03.22", "Shopping: 13.08.22", "Christmas: 24.12.22", "Funeral: 25.12.22"]
print(find_appointment(appointments, "Zoo"))
What you need to use is a dictionary. So like:
appointments = {"Zoo":"11.03.22", "Shopping": "13.08.22"}
something_to_find = "Zoo"
Then to find something in the dictionary:
def find_something(lst, title=" "):
print(lst[title])
find_something(appointments,something_to_find)
So yea you might want to read up on dictionaries. W3schools is a good place to start
[https://www.w3schools.com/python/python_dictionaries.asp]
def find_appointment(lst, title = ""):
for i in lst:
if title in i:
a=title.find(title)
return a
else:
print("No result")
appointments = ["Zoo: 11.03.22", "Shopping: 13.08.22", "Christmas: 24.12.22",
"Funeral: 25.12.22"]
print(find_appointment(appointments, "Zoo"))
#hope this helps. in order to iterate through all elements in list. we have to
use a loop. once the value is found we can get its index by find method of
string.
I have a class that has a dictionary attribute. The dictionary has song titles as its key and a list containing artist, genre and playCount like this:
class library:
def __init__(self,library):
self.library={}
def addSong(self,title,artist,genre,playCount):
self.library[title]=[artist,genre,playCount]
The playCount is an integer. How do I add a 1 to the playCount element without changing any of the other elements. Do I make a new function for it or can I do it without making a function?. Also how can I make a function to print the keys and values of the dictionary as a string like this:
artist, title (genre), playCount
IIUC, you just want to increment playCount every time you pass an existing title to addSong, right?
You can put in an if-else condition in addSong to check if title is in self.library or not and if it exists, then just increment the last element of value of key title by playCount.
Also, to print, it's just a matter of assigning items in their correct positions:
class library:
def __init__(self):
self.library = {}
def addSong(self, title, artist, genre, playCount=1):
if title in self.library:
self.library[title][-1] += playCount
else:
self.library[title] = [artist, genre, playCount]
def get_song_data(self, title):
if title in self.library:
x = self.library[title] + [title]
return "{0}, {3} ({1}), {2}".format(*x)
lib = library()
Output:
lib.addSong('Easy on Me','Adele','ballad',10)
print(lib.get_song_data('Easy on Me')) # Adele, Easy on Me (ballad), 10
lib.addSong('Easy on Me','Adele','ballad',2)
print(lib.get_song_data('Easy on Me')) # Adele, Easy on Me (ballad), 12
lib.addSong('Easy on Me','Adele','ballad')
print(lib.get_song_data('Easy on Me')) # Adele, Easy on Me (ballad), 13
Im working on a project where I created a class named Image.
def __init__(self, name):
self.name = name
self.pixelArray = None
self.key = None
def setPixelArray(self, pixelArray):
self.pixelArray = pixelArray
def setKey(self, key):
self.key = key
Later on i create a list containing Images. And now I want to in a easy way get a specific item out of that list using the name attribute, image.name. Below you can see me getting the imageid that i want to find in the already created image-list.
split = line.split()
imageid = split[0]
key = split[1]
I have been looking around but not found any example of how to do this without a lot of loops. Is there an easy way to rewrite
if x in myList:
do something with x:
I am currently working on an iTunes data program that's cycling constantly through a user's library to get statistics about one's library.
returns
I have a few code snippets like these:
def numArtist(self):
num = 0
for song in self.allSongs:
tempList = []
if song.artist not in tempList:
tempList.append(song.artist)
num += 1
return num
def getAlbumNames(self):
albums = []
for song in self.allSongs:
if song.album not in albums:
albums.append(song.album)
return albums
Where the main for loop body is repeated:
for song in self.allSongs: # same for-loop condition
# different for-loop body
Is there a way to refactor methods like these, where I have the same for loop conditions but with different body definitions?
I have a quite a few methods with the same for-loop, so I'd like to find a way to decrease the complexity and redundancy of my code.
Just for reference, all Song objects have attributes - artist, album (name),genre, etc - that I'm using to get my data.
Use set comprehensions and len to simplify each of them:
def numArtist(self):
return len({song.artist for song in self.allSongs})
def getAlbumNames(self):
return {song.album for song in self.allSongs}
To make it more generic, you could write a method that takes a lambda and use that to filter the property out of each song:
def uniqueProps(self, fxn):
return {fxn(song) for song in self.allSongs}
def getAlbumNames(self):
return self.uniqueProps(lambda song: song.album)
You can use set comprehensions for both snippets, if that counts as a valid "For-Loop refactoring":
artist_count = len({song.artist for song in self.allSongs})
album_names = set({song.album for song in self.allSongs})
Generic version using getattr
get_values = lambda objs, attr: {getattr(obj, attr) for obj in objs
attributes = 'artist', 'album'
values = [get_values(self.allSongs, name) for name in attributes]
artists, albums = values
artist_count = len(artists)
Generic version using lambda
get_artist = lambda song: song.artist
get_album = lambda song: song.album
getters = get_artist, get_album
values = [
{func(song) for song in self.allSongs}
for getter in getters
]
artists, albums = values
artist_count = len(artists)
Generic version using property
# If `song` is an instance of the `Song` class and both `artist` and
# `album` are properties defined on the class, it's also possible to
# directly use the property getter (`property.fget`) to avoid defining
# the lambdas manually:
get_artist = Song.artist.fget
get_album = Song.album.fget
... # <same as above>
If the contents of your allSongs list are immutable - which I suspect they are - you can convert your lists to sets and back to lists again - or use set comprehension - to get rid of duplicates. Then your functions can be greatly simplified like so:
def numArtist(self):
return len({song.artist for sing in self.allSongs})
def getAlbumNames(self):
return list({song.album for song in self.allSongs})
If you're not sure if the song objects are mutable or not, try this out anyway. If they're mutable objects you'll get an exception like:
TypeError: unhashable type: ...
You could try to create the generators, that produces the value of song attributes. Let me give you an example:
def gen_attr(songs, attr_name):
for song in songs:
yield getattr(song, attr_name)
class Song(object):
def __init__(self, name, artist):
self.name = name
self.artist = artist
class Album(object):
def __init__(self, songs_list):
self.songs_list = songs_list
def allSongs(self):
return self.songs_list
s = Song('Ahoy', 'Pirate')
s1 = Song('Bye', 'My Son')
s2 = Song('Ahoy', 'Captain')
a = Album([s, s1])
Now if you want to get all of the song names, u can use:
song_names = list(gen_attr(a.allSongs(), 'name'))
print(song_names) # ['Ahoy', 'Bye', 'Ahoy']
For non-repeated song names you would use:
song_names = list(set(gen_attr(a.allSongs(), 'name')))
print(song_names) # ['Ahoy', 'Bye']
To count the non-repeated artists names, you would use:
artists = len(set(gen_attr(a.allSongs(), 'artist')))
And to create the list of artists, just go for:
artists = list(gen_attr(a.allSongs(), 'artist'))
print(artists) # ['Pirate', 'My Son', 'Captain']
I'm trying to create notifications like facebook has. Everything works all right but I have duplicates. For instance, action = like, url = post/1 I want to get all notifications with status = 1 - unread and eliminate duplicates where action and url are the same. You can find code below I have such error:
error: 'list index out of range' in
if n_dup[i]['url'] == n_dup[j]['url'] and n_dup[i]['action'] == n_dup[j]
def recieve_notification(request):
t = loader.get_template('notifications.html')
nots = Notification.objects.filter(recipent=request.user, status=1, pub_date__gte=datetime.datetime.now()-datetime.timedelta(days=3))
n_dup = [] #list of notifications with duplicates
for n in nots:
n_dup.append({'id':n.id, 'url':n.url, 'action':n.action})
i = len(n_dup)-1
j = len(n_dup)-1
while j>=0:
while i>=0:
if n_dup[i]['url'] == n_dup[j]['url'] and n_dup[i]['action'] == n_dup[j]['action'] and i is not j:
del n_dup[i]
i-=1
j-=1
out_n = []
for n in n_dup:
n_id = n['id']
out_n.append(Notification.objects.get(id=n_id))
c = RequestContext(request, {'notifications':out_n, 'notifications_count':len(out_n)})
return HttpResponse(t.render(c))`
Maybe you are in knowledge of better way to code all this stuff?
On the first iteration of both loops, j == i == len(n_dup)-1, so n_dup[i] == n_dup[j]. It is considered a duplicate and is remove. On the second iteration, you'll try to access n_dub[len(n_dup)-1] which doesn't exist any more, because you removed it.
If I may suggest an alternative approach, lets be lazy and have python do the duplicate detection for us :
class Notification:
def __init__(self, id, url, action):
self.id = id
self.url = url
self.action = action
def __eq__(self, other):
return self.url == other.url and self.action == other.action
def __hash__(self):
return hash(self.url) ^ hash(self.action)
unique_notifications = {Notification(n.id, n.url, n.action) for n in nots}
We define a notification object with a way to compare it and compute a hash (this is needed to put it in a set), and create a set of the notifications. A set never contains duplicate, so you can now iterate over the set !
You can also add this methods to your notification object and use it directly. You would then write :
out_n = set(Notification.objects.filter(...))
Bonus : the algorithm used by the set to remove duplicate is much more efficient than the one you where using.