Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 3 additions & 4 deletions .jules/bolt.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
30 changes: 15 additions & 15 deletions src/lib/matching.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<MatchResult[]>((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");
Expand All @@ -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]
Expand All @@ -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);
}
Expand All @@ -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<ProjectMatch[]>((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[] = [];

Expand Down Expand Up @@ -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);
}
Loading