| 123 | * "how prominent is this property" signal), and caps the list. |
| 124 | */ |
| 125 | export function compactEventProperties( |
| 126 | raw: { |
| 127 | columns: readonly string[]; |
| 128 | properties: Array<{ property_key: string; event_name: string }>; |
| 129 | }, |
| 130 | options: { eventName?: string; max?: number } = {}, |
| 131 | ): { |
| 132 | event_name?: string; |
| 133 | columns: readonly string[]; |
| 134 | properties: string[]; |
| 135 | total: number; |
| 136 | _truncated: boolean; |
| 137 | } { |
| 138 | const { eventName, max = 50 } = options; |
| 139 | const counts = new Map<string, number>(); |
| 140 | for (const row of raw.properties) { |
| 141 | const dot = row.property_key.indexOf('.'); |
| 142 | const root = dot >= 0 ? row.property_key.slice(0, dot) : row.property_key; |
| 143 | counts.set(root, (counts.get(root) ?? 0) + 1); |
| 144 | } |
| 145 | const sorted = Array.from(counts.entries()) |
| 146 | .sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0])) |
| 147 | .map(([key]) => key); |
| 148 | const truncated = sorted.length > max; |
| 149 | return { |
| 150 | ...(eventName ? { event_name: eventName } : {}), |
| 151 | columns: raw.columns, |
| 152 | properties: truncated ? sorted.slice(0, max) : sorted, |
| 153 | total: sorted.length, |
| 154 | _truncated: truncated, |
| 155 | }; |
| 156 | } |
| 157 | |
| 158 | /** |
| 159 | * Known range presets that map to date windows via `getDatesFromRange`. |