Last active
October 12, 2025 09:49
-
-
Save wazeerc/0f84028175ea050ba6c6c07998b4b6b6 to your computer and use it in GitHub Desktop.
Algorithm used to generate a number of recommendations based on selected items
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /** | |
| * Represents any item that can be identified. | |
| */ | |
| interface Identifiable { | |
| id: any; | |
| } | |
| /** | |
| * A generic and advanced recommendation algorithm that works with any data type. | |
| * It uses a content-based filtering approach based on feature extraction and cosine similarity. | |
| * | |
| * @template T - The type of the items, which must have an 'id' property. | |
| * @param allItems - An array of all available items. | |
| * @param selectedItems - An array of items the user has selected. | |
| * @param featureExtractor - A function that converts an item of type T into a numerical vector (number[]). | |
| * @param numberOfRecommendations - The desired number of recommendations. | |
| * @returns An array of recommended items of type T. | |
| */ | |
| const generateGenericRecommendations = <T extends Identifiable>( | |
| allItems: T[], | |
| selectedItems: T[], | |
| featureExtractor: (item: T) => number[], | |
| numberOfRecommendations: number = 3 | |
| ): T[] => { | |
| if (selectedItems.length === 0) { | |
| // Return a default set of items if no selection has been made. | |
| return allItems.slice(0, numberOfRecommendations); | |
| } | |
| // 1. Vectorize all items using the provided feature extractor. | |
| const itemVectors = new Map<any, number[]>( | |
| allItems.map(item => [item.id, featureExtractor(item)]) | |
| ); | |
| // 2. Create a user profile vector by averaging the vectors of selected items. | |
| const selectedVectors = selectedItems.map(item => itemVectors.get(item.id)!); | |
| const userProfileVector: number[] = selectedVectors[0].map((_, i) => | |
| selectedVectors.reduce((sum, vec) => sum + vec[i], 0) / selectedVectors.length | |
| ); | |
| /** | |
| * Calculates the cosine similarity between two numerical vectors. | |
| * @param vecA - The first vector. | |
| * @param vecB - The second vector. | |
| * @returns The cosine similarity score (between -1 and 1). | |
| */ | |
| const cosineSimilarity = (vecA: number[], vecB: number[]): number => { | |
| let dotProduct = 0; | |
| let normA = 0; | |
| let normB = 0; | |
| for (let i = 0; i < vecA.length; i++) { | |
| dotProduct += (vecA[i] || 0) * (vecB[i] || 0); | |
| normA += (vecA[i] || 0) ** 2; | |
| normB += (vecB[i] || 0) ** 2; | |
| } | |
| const denominator = Math.sqrt(normA) * Math.sqrt(normB); | |
| return denominator === 0 ? 0 : dotProduct / denominator; | |
| }; | |
| const selectedItemIds = new Set(selectedItems.map(item => item.id)); | |
| // 3. Calculate similarity scores for all unselected items. | |
| const recommendationsWithScores = allItems | |
| .filter(item => !selectedItemIds.has(item.id)) | |
| .map(item => { | |
| const itemVector = itemVectors.get(item.id)!; | |
| const similarity = cosineSimilarity(userProfileVector, itemVector); | |
| return { item, similarity }; | |
| }); | |
| // 4. Sort by similarity and return the top recommendations. | |
| recommendationsWithScores.sort((a, b) => b.similarity - a.similarity); | |
| return recommendationsWithScores.slice(0, numberOfRecommendations).map(rec => rec.item); | |
| }; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment