The Repeat Visitor
A medium Python interview practice problem on DataDriven. Write the Python and run it against test cases, with instant feedback.
- Domain
- Python
- Difficulty
- medium
- Seniority
- L4
- Asked in
- Vanguard
Problem
The purchase log in purchases is shuffled rather than chronological, with each record a user and a 'YYYY-MM-DD' date. An analyst on the loyalty team wants every repeat visitor, meaning any user with two purchases no more than days apart on the calendar, returned as user IDs sorted alphabetically.
Loyal customers come back sooner than expected.
Example 1
Input:
purchases = [{"date":"2024-01-01","user":"alice"},{"date":"2024-01-05","user":"alice"},{"date":"2024-01-01","user":"bob"},{"date":"2024-01-20","user":"bob"}], days = 7Output:
["alice"]
Example 2
Input:
purchases = [{"date":"2024-03-01","user":"zoe"},{"date":"2024-03-08","user":"zoe"},{"date":"2024-02-10","user":"amy"},{"date":"2024-02-12","user":"amy"}], days = 7Output:
["amy","zoe"]
Worked solution and explanation
What this really is
Underneath the loyalty framing, this is a per-user closest-pair check on a calendar. Group the events by user, then ask whether any two of one user's dates sit within days of each other. Almost everyone gets the grouping right. The two bugs that actually sink candidates are quieter. The first is trusting arrival order and diffing each record against the user's previous one, which drops lee: May 20, May 1, May 22 looks like 19- and 21-day gaps until you notice the 20th and 22nd are two days apart. The second is doing date math on strings or day numbers, which mangles gil's January 28 to February 2 visit. Get either wrong and the loyalty list quietly loses real regulars.
Break down the requirements
Step 1: Group parsed dates by user
Walk the records once and build a dict from each user to their list of parsed dates. Parsing with datetime.strptime up front means every later subtraction is real calendar arithmetic, so month and year boundaries take care of themselves.
Step 2: Put each user's dates in calendar order
Comparing every pair of a user's dates is correct, just quadratic. Sort each user's list instead: once the dates are in order, the tightest gap a user has is always between two neighbors, so one pass over adjacent pairs is enough.
Step 3: Test neighbors against the window
Take (dates[i] - dates[i - 1]).days and compare it with <= days. The boundary is inclusive, and a same-day repeat is a gap of 0. The first qualifying pair settles the user, so break out.
Step 4: Return the visitors alphabetically
Users come out of the dict in whatever order they first appeared, and the second example lists 'zoe' before 'amy' in the log. A final result.sort() produces the alphabetical list the loyalty team reads.
The solution
from datetime import datetime
def repeat_within_window(purchases, days):
user_dates = {}
for p in purchases:
dt = datetime.strptime(p['date'], '%Y-%m-%d')
user_dates.setdefault(p['user'], []).append(dt)
result = []
for uid, dates in user_dates.items():
dates.sort()
for i in range(1, len(dates)):
if (dates[i] - dates[i - 1]).days <= days:
result.append(uid)
break
result.sort()
return resultCommon follow-up questions
- What if the window were measured in hours instead of days? (Tests switching to timestamps with time components and comparing a `timedelta` in hours.)
- What if you needed to count how many repeat visits each user made? (Tests counting all qualifying gaps instead of stopping at the first.)
- How would you express this in SQL? (Tests `LAG` over a per-user ordering, or a self-join with a date-range condition.)
- What if purchase records were streaming in real time, already in order? (Tests keeping only each user's most recent date when records arrive in time order.)