Score queries from daily logs with a half-life and a distinct-user floor, then build a trie that keeps its top k at every node so a keystroke is one walk.
A keystroke has about 100 ms end to end, so typeahead can't rank every query under "s" while the user waits. Day 4 moves the work to build time (sections 2.2 and 3.2):
Implement a function and a class.
score_queries(daily_rows, users, today, blocked, half_life=14, min_users=20) returns {query: score}:
daily_rows holds (day, query, searches, successes) tuples; day and today are datetime.date.normalize() first, so "Samsung S24" and "samsung s24" are one suggestion. users (query → distinct users over 90 days) is keyed by the normalised query.weight = 0.5 ** (age_days / half_life) and score += weight * (searches + 2 * successes).min_users distinct users and queries in blocked (a set of normalised queries).Typeahead(k):
| Method | Does |
|---|---|
build(scores) |
builds the index from {query: score}. Calling it again replaces the old index (a new snapshot) |
suggest(prefix) |
the top k queries starting with normalize(prefix), best score first, ties in alphabetical order. [] for an empty or unknown prefix |
Rules the tests check:
"nike " must not suggest "nikon". The given normalize() keeps one trailing space; your trie simply treats it as a character.suggest must not walk the subtree under the prefix. Keep each node's top-k list ready at build time.In JavaScript and TypeScript it's scoreQueries(dailyRows, users, today, blocked, { halfLife = 14, minUsers = 20 } = {}), returning a Map of query → score. A row is an array [day, query, searches, successes], users is a Map, blocked a Set, and D(y, m, d) gives a UTC-midnight Date, so (today - day) / 86_400_000 is the age in days. new Typeahead(k) has build(scores) (a Map) and suggest(prefix) (an array). "Alphabetical" means plain string order (a < b), as in Python, not localeCompare.
Write your code, then run 3 example tests and 10 hidden tests. Each run is limited to 8 seconds: an infinite loop gets stopped and the runtime restarts, your code stays.