export type SuggestionSource = { description: string; amountCents: number; accountId: number | null; date: string; }; export type Suggestion = { key: string; description: string; /** Most common amount for this description (latest wins ties). */ amountCents: number; /** Account used most recently with this description. */ accountId: number | null; count: number; lastDate: string; }; /** Past descriptions ranked by use, each with its usual amount and latest account. */ export function descriptionSuggestions(rows: SuggestionSource[], today: string): Suggestion[] { const groups = new Map< string, { description: string; amounts: Map; accountId: number | null; count: number; lastDate: string; lastAmount: number; } >(); for (const row of rows) { if (row.date > today) continue; const key = row.description.trim().toLowerCase(); if (!key) continue; const group = groups.get(key) ?? { description: row.description.trim(), amounts: new Map(), accountId: row.accountId, count: 0, lastDate: '', lastAmount: row.amountCents }; group.count++; group.amounts.set(row.amountCents, (group.amounts.get(row.amountCents) ?? 0) + 1); if (row.date >= group.lastDate) { group.lastDate = row.date; group.description = row.description.trim(); group.accountId = row.accountId; group.lastAmount = row.amountCents; } groups.set(key, group); } return [...groups.entries()] .map(([key, group]) => { let amountCents = group.lastAmount; let best = group.amounts.get(amountCents) ?? 0; for (const [value, uses] of group.amounts) if (uses > best) { best = uses; amountCents = value; } return { key, description: group.description, amountCents, accountId: group.accountId, count: group.count, lastDate: group.lastDate }; }) .sort((a, b) => b.count - a.count || b.lastDate.localeCompare(a.lastDate)); }