# By What They Answer To

> Every name reports to its first letter. Sort the roll call as it comes in.

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

Domain: Python · Difficulty: medium · Seniority: L3

## Problem

A stream of event-type names comes off the message bus, and you want them filed into families by the letter they start with. Group `events` by their first character into a dict, keeping each group in the order the names arrived.

## Worked solution and explanation

### What this really is

This is bucket accumulation wearing a grouping costume. The output is a dict of lists, and that shape is the entire point: a list or a set can tell you which first letters showed up, but only a dict of lists can hold, per letter, every name that starts with it in arrival order. Anyone can write the loop. What separates candidates is the first-occurrence case: the moment a key appears for the first time its bucket does not exist yet, and if you index `groups[key]` before creating the list, or write `groups[key] = event` instead of appending, every earlier name that shared that letter vanishes. Get that wrong and each letter keeps only its last name, so every multi-item group quietly collapses to one.

---

### How to get there

#### Step 1: Derive the key

The first character is the partition key. You read it once per name with `event[0]`; the inputs are real names, so there is always a character to read.

#### Step 2: Create-then-append, in one pass

Before appending, make sure the bucket exists. `setdefault(key, [])` returns the existing list or creates an empty one in a single step, then you append. Because you scan left to right and only ever append, within-group order matches input order for free.

---

### The solution

**First-character grouping with setdefault**

```python
def group_events(events):
    groups = {}
    for event in events:
        groups.setdefault(event[0], []).append(event)
    return groups
```

*One pass, one bucket per first letter, appended in arrival order.*

> **Cost**
>
> **Time:** O(n) over the total characters read, dominated by n names each touched once. **Space:** O(n), since every name lands in exactly one bucket. There is no second pass and no sorting.

**Overwrite (wrong)**

groups[key] = event assigns a single name to the key. The second name that shares a letter replaces the first, so 'logout' erases 'login' and the group ends with one element.

**Accumulate (right)**

groups.setdefault(key, []).append(event) keeps a growing list per key, so every name that shares a letter is retained in order.

> **Interviewers watch for**
>
> The tell is how you handle the missing key. `setdefault`, a `defaultdict(list)`, or an explicit `if key not in groups` are all correct; reaching for `groups[key].append` with no guard is the giveaway that a candidate has not thought about the first occurrence.

> **Common pitfall**
>
> If names could be empty strings, `event[0]` raises an IndexError. When the input contract does not promise non-empty names, guard before indexing rather than assuming.

---

## Common follow-up questions

- What if grouping should be case-insensitive? _(Tests normalizing the key with .lower() while preserving the original name.)_
- How would you group by an arbitrary key function instead of the first character? _(Tests accepting a callable so the grouping key is computed by key_fn(item).)_
- What if the returned groups should come back sorted by key? _(Tests wrapping the result in dict(sorted(groups.items())) without disturbing within-group order.)_

## Related

- [All practice problems](https://datadriven.io/problems)
- [Mock interview mode](https://datadriven.io/interview/by_what_they_answer_to)
- [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.