Skip to Content
Course content

174: Practice Exercise: Building a Simple Recommendation Engine

Click on the "Edit" button in the top corner of the screen to edit your slide content.

So, we've spent a lot of time on data structures, but now I want us to actually use them to solve a problem that feels "real." We're going to build a recommendation engine. Now, don't let the term "engine" intimidate you—at its core, a recommendation engine is just a way of saying, "People who liked X also liked Y."

Mapping Out Our Users

I'll start by sketching out some data. For this, a hash of hashes feels right. We have users, and for each user, we have a list of movies they've rated. Let's try this out in the console:

ratings = {
  "Alice" => { "The Matrix" => 5, "Inception" => 4, "Interstellar" => 2 },
  "Bob"   => { "The Matrix" => 5, "Inception" => 5, "The Prestige" => 4 },
  "Charlie" => { "Interstellar" => 5, "The Prestige" => 5, "The Matrix" => 1 }
}

My goal is to recommend a movie to Alice. I'm thinking: if Alice and Bob both love the same movies, then whatever Bob likes that Alice hasn't seen yet is a great candidate for a recommendation. Let's see if I can find who Alice is most similar to.

The Naive Comparison

My first instinct is to just see which users have watched the same movies. In Ruby, the & operator on arrays is great for intersections. Let's try to find Alice's "movie twins":

alice_movies = ratings["Alice"].keys
bob_movies = ratings["Bob"].keys

common = alice_movies & bob_movies
# => ["The Matrix", "Inception"]

That works, but it's too simplistic. Alice and Charlie both watched "The Matrix" and "Interstellar," but Alice hated Interstellar (2) and Charlie loved it (5). If I just look at the keys, Charlie looks like a match, but their tastes are actually opposites. I need to look at the values, not just the keys.

Refining the Similarity Score

I need a way to calculate a "similarity score." I'll try iterating through the movies they both watched and adding to a score if their ratings are close. I'll use a simple difference check here.

def calculate_similarity(user1, user2, data)
  score = 0
  common_movies = data[user1].keys & data[user2].keys
  
  common_movies.each do |movie|
    # If the difference in ratings is 1 or less, they agree
    score += 1 if (data[user1][movie] - data[user2][movie]).abs <= 1
  end
  
  score
end

puts calculate_similarity("Alice", "Bob", ratings)     # => 2
puts calculate_similarity("Alice", "Charlie", ratings) # => 0

That's better. Bob is now clearly the better match for Alice. But wait—what if I have a hundred users? I can't manually call this function for every single person. I'll use each_with_object to build a map of similarity scores for Alice against everyone else.

similarities = ratings.keys.each_with_object({}) do |user, hash|
  next if user == "Alice" # Don't compare Alice to herself
  hash[user] = calculate_similarity("Alice", user, ratings)
end

# similarities => {"Bob" => 2, "Charlie" => 0}

Extracting the Recommendation

Now for the final piece: the actual recommendation. I'll find the person with the highest similarity score, and then I'll look for movies that person rated highly (say, 4 or 5) which Alice hasn't watched yet.

best_match = similarities.max_by { |user, score| score }[0]
# best_match => "Bob"

recommendations = ratings[best_match].select do |movie, rating|
  rating >= 4 && !ratings["Alice"].key?(movie)
end

puts "Because you liked the same things as #{best_match}, you should watch: #{recommendations.keys.join(', ')}"
# => "Because you liked the same things as Bob, you should watch: The Prestige"

It works! It's a primitive system, but the logic is sound: identify shared taste through value comparison, find the strongest correlate, and suggest the delta between their libraries. If I wanted to make this "production-ready," I'd probably look into Pearson Correlation or Cosine Similarity, but for a simple Ruby script, this approach is a great way to handle associative data.




📋 Practical Task

Exercise: The "Bookworm" Recommendation Engine

You are building a book recommendation system for a small library. You have a dataset of users and their ratings (1-5) for various genres of books.

Your Task: Write a script that performs the following:

  • Create a hash called book_ratings with at least 4 users and 5 different book titles. Ensure some users have overlapping tastes and some have opposing tastes.
  • Implement a method find_most_similar_user(target_user, data) that returns the name of the user who has the most similar ratings to the target user. (Tip: Use the absolute difference between ratings as we did in the lesson).
  • Implement a method suggest_books(target_user, data) that:
    1. Finds the most similar user.
    2. Identifies books that the similar user rated 4 or higher, but the target user has not yet read.
    3. Returns an array of those book titles.

Test Case: If "User A" likes "The Hobbit" and "The Fellowship of the Ring," and "User B" also likes both but has also read "The Two Towers," your system should recommend "The Two Towers" to "User A."

Rating
0 0

There are no comments for now.

to be the first to leave a comment.