# No Days Off

> Show up every day. Miss one and the count starts over.

Canonical URL: <https://datadriven.io/problems/no_days_off>

Domain: Python · Difficulty: medium · Seniority: L4

## Problem

You're auditing login records from a retention dashboard, where each entry in `activities` carries a `user_id` and a `date` string in 'YYYY-MM-DD' form, and a user can appear more than once on the same day. Return the `user_id`s who were active across at least `min_streak` (default 3) back-to-back calendar days, treating repeated dates for one user as a single day. The result comes back sorted alphabetically.

## Worked solution and explanation

### What this really is

Strip the retention framing and this is classic gaps-and-islands: per user, find the longest run of consecutive calendar days. Anyone can group by user and sort the dates. The trap is the duplicate date: two logins on the same day are ONE day, and if you skip the dedup you either double-count a day toward the streak or, worse, treat the repeat as a zero-day step and quietly inflate the run. Collapse each user's days into a set of distinct dates first, and the streak scan becomes trivial.

---

### Break down the requirements

#### Step 1: Group activity dates by user

Build a dict mapping each user to their set of unique active dates. The set handles deduplication for free.

#### Step 2: Sort each user's dates

Consecutive detection only makes sense in chronological order, so sort each user's distinct dates.

#### Step 3: Find the longest streak per user

Walk the sorted dates and count the run length. Bump the counter when the next date is exactly one day later; reset to 1 the moment a gap appears.

#### Step 4: Filter users whose streak meets min_streak

Keep only users whose longest run meets min_streak, then return them sorted alphabetically.

---

### The solution

**Group, sort, and streak detection**

```python
from datetime import datetime, timedelta

def find_streak_users(activities: list[dict], min_streak: int = 3) -> list[str]:
    user_dates = {}
    for record in activities:
        user = record['user_id']
        date = datetime.strptime(record['date'], '%Y-%m-%d').date()
        user_dates.setdefault(user, set()).add(date)
    streak_users = []
    for user, dates in user_dates.items():
        sorted_dates = sorted(dates)
        max_streak = 1
        current_streak = 1
        for i in range(1, len(sorted_dates)):
            if sorted_dates[i] - sorted_dates[i - 1] == timedelta(days=1):
                current_streak += 1
            else:
                current_streak = 1
            if current_streak > max_streak:
                max_streak = current_streak
        if max_streak >= min_streak:
            streak_users.append(user)
    return sorted(streak_users)
```

> **Time and Space Complexity**
>
> **Time:** O(n log n), dominated by sorting each user's dates. The grouping pass is O(n).
> 
> **Space:** O(n) for the user-to-dates mapping.

> **Interviewers Watch For**
>
> Whether you deduplicate dates before the streak scan. Two records on the same day must collapse to one day; candidates who skip this quietly inflate streaks and never notice on the happy-path test.

> **Common Pitfall**
>
> Comparing the raw 'YYYY-MM-DD' strings to detect adjacency. String ordering happens to match date ordering, but '2024-01-31' to '2024-02-01' is one day apart and no string trick tells you that. Parse to real date objects and subtract.

---

## Common follow-up questions

- What if the threshold were configurable? _(Tests parameterization: pass the streak length as an argument.)_
- How would you also return the streak dates? _(Tests tracking the start index of the current streak.)_
- What if activity timestamps include hours and you need to bucket by date? _(Tests date truncation from datetime objects.)_
- How would this work on a billion-row event log? _(Tests partitioned processing or database-level window functions.)_

## Related

- [All practice problems](https://datadriven.io/problems)
- [Mock interview mode](https://datadriven.io/interview/no_days_off)
- [Python Interview Questions](https://datadriven.io/python-interview-questions)
- [Data Engineering Interview Prep Guide](https://datadriven.io/data-engineer-interview-prep)
- [Daily Challenge](https://datadriven.io/daily)

---

Source: DataDriven (https://datadriven.io). DataDriven is the data engineering interview community. Live code execution in SQL, Python, and Spark sandboxes. Every feature is open to every member.