# The Bouncer

> Every door has a guest list.

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

Domain: Python · Difficulty: easy · Seniority: L3

## Problem

Implement a permission tracker. The function receives a list of operations, each a 2-element list [op, permission] where op is 'add', 'remove', or 'check'. For each 'check' operation, append True or False (whether the permission is currently granted). Add and remove operations produce no output. Return the list of check results in order.

## Worked solution and explanation

### Why this problem exists in real interviews

Permission checks are the canonical set-membership use case. Interviewers use this prompt to see whether you reach for the right data structure (a `set`), whether you know the difference between `remove` and `discard`, and whether you only emit output for the operation that asks for it.

---

### Break down the requirements

#### Step 1: Pick a set, not a list

Permissions are unordered and unique. A `set` gives O(1) average add, remove, and membership test. A `list` would force O(n) scans on every check.

#### Step 2: Only append to results on `check`

The harness compares your returned list to a parallel list of `check` outcomes. `add` and `remove` produce no output, so do not append `None` or anything else for them.

#### Step 3: Use `discard` to tolerate idempotent removes

If `remove` is called for a permission that was never granted, `set.remove` raises `KeyError`. `set.discard` silently no-ops, which matches the real-world behavior of most ACL systems.

---

### The solution

**Single-pass set-backed permission tracker**

```python
def permissions_demo(ops: list[list]) -> list:
    granted = set()
    results = []
    for op, permission in ops:
        if op == 'add':
            granted.add(permission)
        elif op == 'remove':
            granted.discard(permission)
        elif op == 'check':
            results.append(permission in granted)
    return results
```

> **Cost Analysis**
>
> Time: O(1) average per operation, O(N) for N total ops. Space: O(K) where K is the number of distinct currently-granted permissions, plus O(C) for the C `check` outputs.

> **Interviewers Watch For**
>
> Whether you choose `set` over `list` or `dict`, whether you use `discard` instead of `remove` (and can explain why), and whether the returned list length equals the number of `check` ops, not the total operation count. Many candidates accidentally append for every op.

> **Common Pitfall**
>
> Calling `granted.remove(permission)` and crashing on a remove of a never-added permission. The fix is `discard`, which treats removal as idempotent. The other common bug is appending `None` for non-check ops, which puts garbage into the results list.

---

## Common follow-up questions

- How would you support `check` returning the timestamp the permission was granted, not just True/False? _(Tests promotion from `set` to `dict`. The structure becomes `{permission: granted_at_ts}` and `check` returns the timestamp or `None`.)_
- What if permissions have a hierarchy where `admin` implies `read` and `write`? _(Tests how the candidate models implication. Options include expanding implied permissions on add, computing closure on check, or keeping a separate role->permissions map.)_
- How would you make this safe across many threads adding and checking concurrently? _(Tests concurrency. A `threading.Lock` wraps the whole op; for higher throughput, a copy-on-write set or per-key locking starts to make sense.)_

## Related

- [All practice problems](https://datadriven.io/problems)
- [Mock interview mode](https://datadriven.io/interview/the_bouncer)
- [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). 100% free data engineering interview prep. Live code execution against Postgres 16, Python 3.11, and Spark sandboxes. No paywall, no premium tier, no signup gate.