-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Methods and Blocks
-
Section 4: Object-Oriented Ruby
-
Section 5: Metaprogramming
-
Section 6: Working with Data and Files
-
Section 7: Ruby Frameworks Overview
-
Section 8: Ecosystem and Testing
-
Section 9: Practical Projects
-
Section 10: Interview Practice
-
Section 11: Data Structures and Algorithms in Ruby
-
Section 12: More Practice Exercises
-
Section 13: Enumerable and Functional Style
-
Section 14: More OOP Practice
-
Section 15: More Testing
-
Section 16: Enumerable and Comparable Modules In Depth
-
Section 17: Ruby Standard Library: Core Utilities
-
Section 18: Ruby Standard Library: Data and Security
-
Section 19: Ruby Standard Library: CLI and Text
-
Section 20: Ruby Networking
-
Section 21: Ruby on Rails Deep Dive
-
Section 22: Ruby Metaprogramming Deep Dive
-
Section 23: Ruby Design Patterns
-
Section 24: Ruby Concurrency
-
Section 25: Ruby Testing Deep Dive
-
Section 26: Ruby Gems and Packaging
-
Section 27: Ruby Performance
-
Section 28: More Practice Exercises
-
Section 29: More Interview Practice
-
Section 30: Rails API Development
-
Section 31: Rails Authentication and Authorization
-
Section 32: Rails Testing Deep Dive
-
Section 33: Rails Performance
-
Section 34: Rails Deployment
-
Section 35: Sinatra and Lightweight Ruby Web Apps
-
Section 36: More Ruby Language Deep Dive
-
Section 37: Ruby 3.x Modern Features
-
Section 38: More Data Structures in Ruby
-
Section 39: More Practical Projects
-
Section 40: Ruby Ecosystem Tools
-
Section 41: More Practice and Review
-
Section 42: Final Practice and Mastery
-
Section 43: Ruby Interview Deep Dive
-
Section 44: Ruby Background Processing Deep Dive
-
Section 45: Ruby GraphQL
-
Section 46: Ruby Object Model Deep Dive
-
Section 47: Ruby Hanami Framework Overview
-
Section 48: Ruby gRPC and Protocol Buffers
-
Section 49: Ruby Data Processing
-
Section 50: Ruby Search Integration
-
Section 51: Ruby File Upload and Media
-
Section 52: Ruby Email and Notifications
-
Section 53: Ruby Admin Panels
-
Section 54: Ruby Feature Flags and Experimentation
-
Section 55: Ruby Monitoring and Observability
-
Section 56: Ruby Docker and Deployment Deep Dive
-
Section 57: Ruby Security Deep Dive
-
Section 58: More Advanced Metaprogramming
-
Section 59: More Final Projects
174: Practice Exercise: Building a Simple Recommendation Engine
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_ratingswith 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:- Finds the most similar user.
- Identifies books that the similar user rated 4 or higher, but the target user has not yet read.
- 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."
There are no comments for now.