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 = 7

Output:

["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 = 7

Output:

["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

Group by user, order the dates, check neighbors
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 result

Common follow-up questions