@@ -105,6 +105,7 @@ const CATEGORY_COLORS: Record<string, string> = {
105105 "Autres profils" : "bg-chart-5" ,
106106} ;
107107
108+ // ⚡ Bolt: Consolidated multiple iterations into a single O(n) pass over profiles.
108109function computeStats ( profiles : RawProfile [ ] ) : ComputedStats {
109110 const total = profiles . length ;
110111 if ( total === 0 ) {
@@ -123,9 +124,64 @@ function computeStats(profiles: RawProfile[]): ComputedStats {
123124 }
124125
125126 const roleCounts : Record < string , number > = { } ;
126- for ( const p of profiles )
127+ const expCounts : Record < string , number > = { } ;
128+ const techCounts : Record < string , number > = { } ;
129+ const cityCounts : Record < string , number > = { } ;
130+
131+ let collab = 0 ;
132+ let activeLastWeek = 0 ;
133+
134+ const now = new Date ( ) ;
135+ const weekAgo = new Date ( now . getTime ( ) - 7 * 24 * 60 * 60 * 1000 ) ;
136+
137+ const months = Array . from ( { length : 6 } , ( _ , i ) => {
138+ const d = new Date ( now . getFullYear ( ) , now . getMonth ( ) - 5 + i , 1 ) ;
139+ return {
140+ month : d . toLocaleDateString ( "fr-FR" , { month : "short" } ) ,
141+ year : d . getFullYear ( ) ,
142+ monthNum : d . getMonth ( ) ,
143+ count : 0 ,
144+ } ;
145+ } ) ;
146+
147+ // ⚡ Bolt: Single pass loop for all aggregations
148+ for ( const p of profiles ) {
149+ // Roles
127150 roleCounts [ p . role_type ] = ( roleCounts [ p . role_type ] || 0 ) + 1 ;
128151
152+ // Experience
153+ expCounts [ p . experience_level ] = ( expCounts [ p . experience_level ] || 0 ) + 1 ;
154+
155+ // Techs
156+ if ( p . tech_stack ) {
157+ for ( const t of p . tech_stack ) {
158+ techCounts [ t ] = ( techCounts [ t ] || 0 ) + 1 ;
159+ }
160+ }
161+
162+ // Cities
163+ if ( p . city ) {
164+ cityCounts [ p . city ] = ( cityCounts [ p . city ] || 0 ) + 1 ;
165+ }
166+
167+ // Growth
168+ const d = new Date ( p . created_at ) ;
169+ const entry = months . find (
170+ ( m ) => m . year === d . getFullYear ( ) && m . monthNum === d . getMonth ( )
171+ ) ;
172+ if ( entry ) entry . count ++ ;
173+
174+ // Collaboration
175+ if ( p . open_to_collaboration ) {
176+ collab ++ ;
177+ }
178+
179+ // Active last week
180+ if ( p . last_seen_at && new Date ( p . last_seen_at ) >= weekAgo ) {
181+ activeLastWeek ++ ;
182+ }
183+ }
184+
129185 // Regroupement dynamique : agrège tous les role_type connus dans des
130186 // catégories plus larges, n'affiche que celles ayant au moins 1 profil
131187 // et trie par population décroissante.
@@ -143,9 +199,6 @@ function computeStats(profiles: RawProfile[]): ComputedStats {
143199 . filter ( ( r ) => r . pct > 0 )
144200 . sort ( ( a , b ) => b . pct - a . pct ) ;
145201
146- const expCounts : Record < string , number > = { } ;
147- for ( const p of profiles )
148- expCounts [ p . experience_level ] = ( expCounts [ p . experience_level ] || 0 ) + 1 ;
149202 const experience = [
150203 {
151204 label : "Junior" ,
@@ -164,10 +217,6 @@ function computeStats(profiles: RawProfile[]): ComputedStats {
164217 } ,
165218 ] ;
166219
167- const techCounts : Record < string , number > = { } ;
168- for ( const p of profiles )
169- for ( const t of p . tech_stack ?? [ ] )
170- techCounts [ t ] = ( techCounts [ t ] || 0 ) + 1 ;
171220 const techs = Object . entries ( techCounts )
172221 . sort ( ( a , b ) => b [ 1 ] - a [ 1 ] )
173222 . slice ( 0 , 6 )
@@ -176,9 +225,6 @@ function computeStats(profiles: RawProfile[]): ComputedStats {
176225 pct : Math . round ( ( count / total ) * 100 ) ,
177226 } ) ) ;
178227
179- const cityCounts : Record < string , number > = { } ;
180- for ( const p of profiles )
181- if ( p . city ) cityCounts [ p . city ] = ( cityCounts [ p . city ] || 0 ) + 1 ;
182228 const sortedCities = Object . entries ( cityCounts ) . sort ( ( a , b ) => b [ 1 ] - a [ 1 ] ) ;
183229 const cities = sortedCities
184230 . slice ( 0 , 3 )
@@ -187,49 +233,15 @@ function computeStats(profiles: RawProfile[]): ComputedStats {
187233 if ( otherCount > 0 )
188234 cities . push ( { name : "Autres" , pct : Math . round ( ( otherCount / total ) * 100 ) } ) ;
189235
190- const now = new Date ( ) ;
191- const months = Array . from ( { length : 6 } , ( _ , i ) => {
192- const d = new Date ( now . getFullYear ( ) , now . getMonth ( ) - 5 + i , 1 ) ;
193- return {
194- month : d . toLocaleDateString ( "fr-FR" , { month : "short" } ) ,
195- year : d . getFullYear ( ) ,
196- monthNum : d . getMonth ( ) ,
197- count : 0 ,
198- } ;
199- } ) ;
200- for ( const p of profiles ) {
201- const d = new Date ( p . created_at ) ;
202- const entry = months . find (
203- ( m ) => m . year === d . getFullYear ( ) && m . monthNum === d . getMonth ( )
204- ) ;
205- if ( entry ) entry . count ++ ;
206- }
207236 const maxCount = Math . max ( ...months . map ( ( m ) => m . count ) , 1 ) ;
208237 const growth = months . map ( ( m ) => ( {
209238 month : m . month ,
210239 value : Math . round ( ( m . count / maxCount ) * 100 ) ,
211240 } ) ) ;
212241
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 > ( ) ;
219- const weekAgo = new Date ( Date . now ( ) - 7 * 24 * 60 * 60 * 1000 ) ;
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- }
242+ // ⚡ Bolt: Derive distinct counts from keys instead of doing additional passes
243+ const totalCities = Object . keys ( cityCounts ) . length ;
244+ const allTechs = Object . keys ( techCounts ) . length ;
233245
234246 return {
235247 total,
0 commit comments