Skip to content

Commit f8fd2e5

Browse files
⚡ Bolt: Optimize profile data aggregation with single O(n) pass
Co-authored-by: KxlSys <116387953+KxlSys@users.noreply.github.com>
1 parent 7dc0352 commit f8fd2e5

5 files changed

Lines changed: 76 additions & 18 deletions

File tree

.jules/bolt.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,7 @@
1717
## 2024-06-25 - DebouncedInput Component Memoization
1818
**Learning:** The `DebouncedInput` component was used in several places to prevent excessive parent re-renders while typing. However, because `DebouncedInput` itself was not wrapped in `React.memo()`, it was still re-rendering unnecessarily whenever its parent components re-rendered for reasons unrelated to the input (e.g. other form fields changing, complex page state updates). This unnecessary re-rendering could destroy and recreate its internal debouncing logic and cause extra reconciliation work.
1919
**Action:** When creating utility wrapper components designed to optimize performance (like `DebouncedInput`), ensure the component itself is memoized with `React.memo()` so it fully shields itself from irrelevant parent state updates.
20+
21+
## 2024-07-10 - O(n) Single Pass Data Aggregation
22+
**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.
23+
**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.

src/components/hero/hero-section.tsx

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,25 @@ interface HeroSectionProps {
1111
export function HeroSection({ totalContributors, profiles }: HeroSectionProps) {
1212
const { user } = useAuthStore();
1313

14-
const uniqueCities = new Set(profiles.map((p) => p.city).filter(Boolean)).size;
15-
const uniqueTechs = new Set(profiles.flatMap((p) => p.tech_stack)).size;
16-
const openToCollab = profiles.filter((p) => p.open_to_collaboration).length;
14+
// ⚡ Bolt: Consolidate data aggregation into a single O(n) pass
15+
// Avoids multiple redundant iterations and intermediate array allocations
16+
const citySet = new Set<string>();
17+
const techSet = new Set<string>();
18+
let openToCollab = 0;
19+
20+
for (let i = 0; i < profiles.length; i++) {
21+
const p = profiles[i];
22+
if (p.city) citySet.add(p.city);
23+
if (p.open_to_collaboration) openToCollab++;
24+
if (p.tech_stack) {
25+
for (let j = 0; j < p.tech_stack.length; j++) {
26+
techSet.add(p.tech_stack[j]);
27+
}
28+
}
29+
}
30+
31+
const uniqueCities = citySet.size;
32+
const uniqueTechs = techSet.size;
1733

1834
return (
1935
<section className="ambient-bg relative overflow-hidden border-b border-white/10">

src/hooks/use-platform-stats.ts

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,29 @@ function computeStats(
1414
): PlatformStats {
1515
const total = profiles.length;
1616
if (total === 0) return { totalUsers: 0, totalCities: 0, collaborationRate: 0, techCount: 0 };
17-
const cities = new Set(profiles.map((p) => p.city).filter(Boolean)).size;
18-
const collab = profiles.filter((p) => p.open_to_collaboration).length;
19-
const allTechs = new Set(profiles.flatMap((p) => p.tech_stack ?? [])).size;
17+
18+
// ⚡ Bolt: Consolidate data aggregation into a single O(n) pass
19+
// Avoids multiple redundant iterations and intermediate array allocations
20+
const citySet = new Set<string>();
21+
const techSet = new Set<string>();
22+
let collab = 0;
23+
24+
for (let i = 0; i < total; i++) {
25+
const p = profiles[i];
26+
if (p.city) citySet.add(p.city);
27+
if (p.open_to_collaboration) collab++;
28+
if (p.tech_stack) {
29+
for (let j = 0; j < p.tech_stack.length; j++) {
30+
techSet.add(p.tech_stack[j]);
31+
}
32+
}
33+
}
34+
2035
return {
2136
totalUsers: total,
22-
totalCities: cities,
37+
totalCities: citySet.size,
2338
collaborationRate: Math.round((collab / total) * 100),
24-
techCount: allTechs,
39+
techCount: techSet.size,
2540
};
2641
}
2742

src/pages/about-page.tsx

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -210,19 +210,32 @@ function computeStats(profiles: RawProfile[]): ComputedStats {
210210
value: Math.round((m.count / maxCount) * 100),
211211
}));
212212

213-
const collab = profiles.filter((p) => p.open_to_collaboration).length;
214-
const totalCities = new Set(profiles.map((p) => p.city).filter(Boolean)).size;
215-
const allTechs = new Set(profiles.flatMap((p) => p.tech_stack ?? [])).size;
213+
// ⚡ Bolt: Consolidate data aggregation into a single O(n) pass
214+
// Avoids multiple redundant iterations and intermediate array allocations
215+
let collab = 0;
216+
let activeLastWeek = 0;
217+
const citySet = new Set<string>();
218+
const techSet = new Set<string>();
216219
const weekAgo = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000);
217-
const activeLastWeek = profiles.filter(
218-
(p) => p.last_seen_at && new Date(p.last_seen_at) >= weekAgo
219-
).length;
220+
221+
for (let i = 0; i < profiles.length; i++) {
222+
const p = profiles[i];
223+
if (p.open_to_collaboration) collab++;
224+
if (p.city) citySet.add(p.city);
225+
if (p.last_seen_at && new Date(p.last_seen_at) >= weekAgo) activeLastWeek++;
226+
227+
if (p.tech_stack) {
228+
for (let j = 0; j < p.tech_stack.length; j++) {
229+
techSet.add(p.tech_stack[j]);
230+
}
231+
}
232+
}
220233

221234
return {
222235
total,
223236
collabRate: Math.round((collab / total) * 100),
224-
totalCities,
225-
allTechs,
237+
totalCities: citySet.size,
238+
allTechs: techSet.size,
226239
activeLastWeek,
227240
roles,
228241
techs,

src/pages/admin-page.tsx

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -177,10 +177,20 @@ export function AdminPage() {
177177
return p.full_name.toLowerCase().includes(q) || p.username.toLowerCase().includes(q) || p.city?.toLowerCase().includes(q);
178178
});
179179

180+
// ⚡ Bolt: Consolidate data aggregation into a single O(n) pass
181+
// Avoids multiple redundant iterations and intermediate array allocations
182+
const citySet = new Set<string>();
183+
let collaboratingCount = 0;
184+
for (let i = 0; i < profiles.length; i++) {
185+
const p = profiles[i];
186+
if (p.city) citySet.add(p.city);
187+
if (p.open_to_collaboration) collaboratingCount++;
188+
}
189+
180190
const stats = {
181191
total: profiles.length,
182-
cities: new Set(profiles.map((p) => p.city).filter(Boolean)).size,
183-
collaborating: profiles.filter((p) => p.open_to_collaboration).length,
192+
cities: citySet.size,
193+
collaborating: collaboratingCount,
184194
};
185195
const collabRate = stats.total > 0 ? (stats.collaborating / stats.total) * 100 : 0;
186196
const pendingInvitations = invitations.filter((i) => i.status === "pending").length;

0 commit comments

Comments
 (0)