Skip to content
All essays
WebJanuary 15, 20265 min

Building Recommendation Engines

Complete guide to building recommendation systems using collaborative filtering and content-based filtering

Ü
Ümit Uz
Mobile & Full Stack Developer

Recommendation engines power personalized experiences. This guide covers collaborative and content-based filtering approaches.

Collaborative Filtering

User-Based Filtering

Find similar users and recommend their preferences:

typescript
export function findSimilarUsers(userId: string, ratings: Map<string, number[]>) {
  const userRatings = ratings.get(userId)!;
  const similarities = new Map<string, number>();

  for (const [otherId, otherRatings] of ratings) {
    if (otherId === userId) continue;

    const similarity = cosineSimilarity(userRatings, otherRatings);
    similarities.set(otherId, similarity);
  }

  return similarities;
}

function cosineSimilarity(a: number[], b: number[]): number {
  const dotProduct = a.reduce((sum, val, i) => sum + val * b[i], 0);
  const magnitudeA = Math.sqrt(a.reduce((sum, val) => sum + val * val, 0));
  const magnitudeB = Math.sqrt(b.reduce((sum, val) => sum + val * val, 0));

  return dotProduct / (magnitudeA * magnitudeB);
}

Content-Based Filtering

Item Similarity

typescript
interface ItemFeatures {
  category: string;
  tags: string[];
  price: number;
}

export function itemSimilarity(item1: ItemFeatures, item2: ItemFeatures): number {
  let score = 0;

  // Category match
  if (item1.category === item2.category) score += 0.5;

  // Tag overlap
  const commonTags = item1.tags.filter(t => item2.tags.includes(t));
  score += (commonTags.length / Math.max(item1.tags.length, item2.tags.length)) * 0.3;

  // Price similarity
  const priceDiff = Math.abs(item1.price - item2.price);
  score += Math.max(0, 1 - priceDiff / 100) * 0.2;

  return score;
}

Hybrid Approach

Combine both methods:

typescript
export function getRecommendations(userId: string) {
  const collaborative = getCollaborativeRecommendations(userId);
  const contentBased = getContentBasedRecommendations(userId);

  // Weighted combination
  return collaborative
    .map(item => ({ item, score: item.score * 0.6 }))
    .concat(contentBased.map(item => ({ item, score: item.score * 0.4 })))
    .sort((a, b) => b.score - a.score)
    .slice(0, 10);
}

Conclusion

Collaborative filtering works well with sufficient user data. Content-based filtering is better for cold starts. Combine both for optimal results.

Enjoyed this essay? Share it.

Related essays

Next essay
Advanced Collaborative Filtering Techniques