( valueExtractor: (value: T) => number = (v) => v as unknown as number, )
| 279 | * @param valueExtractor Function to extract a numeric value from each data entry |
| 280 | */ |
| 281 | export function median<T>( |
| 282 | valueExtractor: (value: T) => number = (v) => v as unknown as number, |
| 283 | ): AggregateFunction<T, number, Array<number>> { |
| 284 | return { |
| 285 | preMap: (data: T) => [valueExtractor(data)], |
| 286 | reduce: (values: Array<[Array<number>, number]>) => { |
| 287 | // Flatten all values, taking multiplicity into account |
| 288 | const allValues: Array<number> = [] |
| 289 | for (const [valueArray, multiplicity] of values) { |
| 290 | for (const value of valueArray) { |
| 291 | // Add each value multiple times based on multiplicity |
| 292 | for (let i = 0; i < multiplicity; i++) { |
| 293 | allValues.push(value) |
| 294 | } |
| 295 | } |
| 296 | } |
| 297 | |
| 298 | // Return empty array if no values |
| 299 | if (allValues.length === 0) { |
| 300 | return [] |
| 301 | } |
| 302 | |
| 303 | // Sort values |
| 304 | allValues.sort((a, b) => a - b) |
| 305 | |
| 306 | return allValues |
| 307 | }, |
| 308 | postMap: (result: Array<number>) => { |
| 309 | if (result.length === 0) return 0 |
| 310 | |
| 311 | const mid = Math.floor(result.length / 2) |
| 312 | |
| 313 | // If even number of values, average the two middle values |
| 314 | if (result.length % 2 === 0) { |
| 315 | return (result[mid - 1]! + result[mid]!) / 2 |
| 316 | } |
| 317 | |
| 318 | // If odd number of values, return the middle value |
| 319 | return result[mid]! |
| 320 | }, |
| 321 | } |
| 322 | } |
| 323 | |
| 324 | /** |
| 325 | * Creates a mode aggregate function that computes the most frequent value in a group |
no test coverage detected