diff --git a/.jules/bolt.md b/.jules/bolt.md index 76669aa..57d9129 100644 --- a/.jules/bolt.md +++ b/.jules/bolt.md @@ -21,7 +21,6 @@ ## 2024-07-10 - O(n) Single Pass Data Aggregation **Learning:** Using chained array methods (e.g. `.map().filter()`, `.flatMap()`, and repeated `.filter()` over the same large list like `profiles`) leads to redundant O(n) passes over data and allocates unnecessary intermediate arrays. This blocks the main thread longer than necessary when calculating basic statistics like `uniqueCities`, `uniqueTechs`, or total counts. **Action:** Consolidate statistical aggregations over large object arrays into a single O(n) `for` loop that updates multiple accumulators (like `Set` objects and primitive counters) in one pass, thereby improving performance and reducing memory overhead. - -## 2024-07-18 - Component Map Memoization Pattern -**Learning:** In React components with highly interactive state (like buttons triggering local state changes such as `visibleCount`), mapping over raw datasets to create derived UI objects outside of a `useMemo` block leads to unnecessary O(N) object allocations and garbage collection on every render. -**Action:** When a component derives lists from props or state for rendering, wrap the mapping or filtering logic inside `useMemo` to cache the result, recalculating only when the underlying data changes, thereby freeing up the main thread from redundant object allocations. +## 2024-07-20 - Consolidate array operations in matching algorithms +**Learning:** Chaining multiple array methods (`.map().filter()`, `.flatMap()`) creates unnecessary intermediate arrays and O(N) passes over the data. +**Action:** Use a single `reduce` or `for` loop with multiple accumulators to filter and transform the data simultaneously before sorting. This improves performance for data aggregation, particularly on large arrays like projects or candidates. diff --git a/src/lib/matching.ts b/src/lib/matching.ts index 82a0907..6bb9732 100644 --- a/src/lib/matching.ts +++ b/src/lib/matching.ts @@ -46,18 +46,19 @@ export function calculateMatches( ): MatchResult[] { // ⚡ Bolt: Pre-calculate the current user's tech stack as a Set to avoid O(N*M) lookups const currentUserTechsSet = new Set(currentUser.tech_stack); - const complementary = COMPLEMENTARY_ROLES[currentUser.role_type] || []; - const levelMap = { junior: 1, mid: 2, senior: 3 }; + const results: MatchResult[] = []; - // ⚡ Bolt: Consolidate map and filter into a single reduce pass - const results = candidates.reduce((acc, candidate) => { + // ⚡ Bolt: Consolidate .filter().map().filter() into a single O(n) loop to reduce allocations + for (let i = 0; i < candidates.length; i++) { + const candidate = candidates[i]; if (candidate.id === currentUser.id || !candidate.open_to_collaboration) { - return acc; + continue; } let score = 0; const reasons: string[] = []; + const complementary = COMPLEMENTARY_ROLES[currentUser.role_type] || []; if (complementary.includes(candidate.role_type)) { score += 30; reasons.push("Competences complementaires"); @@ -81,6 +82,7 @@ export function calculateMatches( reasons.push("Meme ville"); } + const levelMap = { junior: 1, mid: 2, senior: 3 }; const diff = Math.abs( levelMap[currentUser.experience_level] - levelMap[candidate.experience_level] @@ -95,11 +97,9 @@ export function calculateMatches( } if (score > 0) { - acc.push({ profile: candidate, score: Math.min(score, 100), reasons }); + results.push({ profile: candidate, score: Math.min(score, 100), reasons }); } - - return acc; - }, []); + } return results.sort((a, b) => b.score - a.score); } @@ -111,9 +111,11 @@ export function calculateProjectMatches( // ⚡ Bolt: Pre-calculate the lowercase tech stack of the profile to avoid re-lowercasing // it for every single tech in every single project. const profileTechsLower = profile.tech_stack.map((t) => t.toLowerCase()); + const results: ProjectMatch[] = []; - // ⚡ Bolt: Consolidate map and filter into a single reduce pass - const results = projects.reduce((acc, project) => { + // ⚡ Bolt: Consolidate .map().filter() into a single O(n) loop to reduce intermediate allocations + for (let i = 0; i < projects.length; i++) { + const project = projects[i]; let score = 0; const reasons: string[] = []; @@ -163,11 +165,9 @@ export function calculateProjectMatches( } if (score > 0) { - acc.push({ project, score: Math.min(score, 100), reasons }); + results.push({ project, score: Math.min(score, 100), reasons }); } - - return acc; - }, []); + } return results.sort((a, b) => b.score - a.score); }