Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ada AC2 Ocelots Class - Elaine(SiOk) Sohn #13

Open
wants to merge 11 commits into
base: master
Choose a base branch
from

Conversation

sioksohn
Copy link

No description provided.

Copy link

@kelsey-steven-ada kelsey-steven-ada left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎉 Congrats on completing your first project at Ada! 🎉 I’ve added some suggestions & questions, please let me know if there's anything I can clarify.

Comment on lines +125 to +127
assert updated_data["watched"][0]["title"] == MOVIE_TITLE_1
assert updated_data["watched"][0]["genre"] == GENRE_1
assert updated_data["watched"][0]["rating"] == RATING_1

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice assertions for all the relevant movie data!

Comment on lines +152 to +153
assert movie_to_watch not in updated_data["watchlist"]
assert movie_to_watch in updated_data["watched"]

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great use of movie_to_watch to confirm the object we set up in the # Arrange step is in the watched list and was removed from watchlist!

@@ -1,23 +1,129 @@
# ------------- WAVE 1 --------------------

def create_movie(title, genre, rating):
pass
if title == None or genre == None or rating == None:

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice handling to return None for missing data! In Python, when we test if a variable is pointing to None the PEP8 style guide recommends using is or is not for comparison rather than operators like ==.

None is a special object in Python, it’s a singleton, meaning there is only ever one instance of None in memory. All variables holding None are actually pointing to the same instance. By using is to compare against None, we avoid issues that could be caused by a class’s custom implementation of the comparison operators, and we gain some speed over a check using == since we are checking against identity rather than looking up if the variable's held type has a comparison function that should be used.

if title is None or genre is None or rating is None :
    return None

If you’d like more info on is vs == for None, here’s a couple resources to get started =]
https://peps.python.org/pep-0008/#programming-recommendations
http://jaredgrubb.blogspot.com/2009/04/python-is-none-vs-none.html

Something else to consider: here we're explicitly checking if the values are None, but what if we got an empty string for movie_title? If we wanted to check for empty strings at the same time as we check for None, we could rely on the truthy/falsy values of the parameters and write something like:

if not title or not genre or not rating:
    return None

Comment on lines +17 to +20
for movie in user_data["watchlist"]:
if movie["title"] == title:
user_data["watchlist"].remove(movie)
user_data["watched"].append(movie)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function works, but it contains an "anti-pattern" that is important to be aware of. An element of the list that is being iterated over is being removed inside the for loop. This won't cause problems in this particular case, but consider this slightly different implementation:

def watch_movie(user_data, title):
    for i in range(len(user_data["watchlist"])):
        item = user_data["watchlist"][i]

        if item["title"] == title:
            user_data["watched"].append(item)
            user_data["watchlist"].remove(item)

    return user_data

If we were to test this in a scenario where user_data["watchlist"] has 2 elements, and the first element is the movie that we are moving to user_data["watched"], during the second iteration through the loop it will throw an IndexError, because we will try to access the 2nd element in user_data["watchlist"], but there no longer is a second element.

This is a coding pattern that I recommend avoiding because it has so much potential for unforeseen errors. One fix is to return immediately after making the swap (ie, add return user_data inside the if-block so that the loop ends as soon as the swap is made). Another fix is to use the loop to find the item, but not make the swap until after the loop ends.


# -----------------------------------------
# ------------- WAVE 2 --------------------
# -----------------------------------------
def get_watched_avg_rating(user_data):
count, total_rating = 0, 0.0

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would consider removing count. We can build up that value over the loop, but we can get that same value by calling len(user_data["watched"]).

Comment on lines +42 to +43
sorted_genres = sorted(genres.items(), key=lambda x:x[1], reverse=True)
return sorted_genres[0][0] if len(sorted_genres) > 0 else None

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great use of a frequency map genres to count the genre occurrences! Another way we could get the maximum value out of genres is with the max function:

max_value = max(genres, key=genres.get)
return max_value

Comment on lines +49 to +64
user_movies, friend_movies = set(), set()

for user_movie in user_data["watched"]:
user_movies.add(user_movie["title"])

for friend_dict in user_data["friends"]:
for friend_movie in friend_dict["watched"]:
friend_movies.add(friend_movie["title"])
unique_title = user_movies - friend_movies

unique_movies = []
for movie in user_data["watched"]:
if movie["title"] in unique_title and movie not in unique_movies:
unique_movies.append(movie)

return unique_movies

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Really neat approach using sets!

Something to think about, here we will create 3 sets and a list, and we will iterate over user_data["watched"] twice and all data in user_data["friends"] once.

If we only create a set of the friend's titles, we can get the benefits of quick look up using a set, and make a single pass over user_data["watched"].

friend_movies = set()
for friend in user_data["friends"]:
    for movie in friend["watched"]:
        friend_movies.add(movie["title"])

unique_movies = []
for movie in user_data["watched"]:
    if movie["title"] not in friends_movie_set:
        unique_movies.append(movie)

return unique_movies


recommended = []
for friend in user_data["friends"]:
for movie in friend["watched"]:

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like the indentation is too deep in this for loop, you might have indented twice.

recommended = []
for friend in user_data["friends"]:
for movie in friend["watched"]:
if movie in get_friends_unique_watched(user_data):

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Love to see the code reuse!

This implementation will call get_friends_unique_watched in every iteration of the loop. Since our input data, and thus the result of calling get_friends_unique_watched, shouldn't be changing, we could reduce the processing time of our function by creating a variable that holds the result of get_friends_unique_watched before the loop starts. Because get_friends_unique_watched loops over our friend data already and provides us a list of movies that our user hasn't seen, we could also remove the for loops through the friend data here:

unique_friend_movies = get_friends_unique_watched(user_data)
recommended = []

for movie in unique_friend_movies:
    if movie['host'] in user_data['subscriptions']:
        recommended.append(movie)

return recommended

Comment on lines +120 to +125
rec_movies = []
for fav_movie in user_data["favorites"]:
for uniq_movie in get_unique_watched(user_data):
if fav_movie["title"] == uniq_movie["title"]:
rec_movies.append(fav_movie)
return rec_movies

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We're nesting loops a little more than required here. We could remove the loop over user_data["favorites"] and keep the loop over the result of get_unique_watched. Then in each iteration, we'd check if the current move is in user_data['favorites'].

for uniq_movie in get_unique_watched(user_data):
    if uniq_movie in user_data['favorites']:
        rec_movies.append(uniq_movie)

return rec_movies

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants