Coding sample solution

Coding: rank agents by rating

How to talk through this coding question, from a first idea to working code, in an interview.

Coding interview · Sample solution in Python · Independent, not affiliated with Atlassian

The coding question

Build a rating system for customer support agents. Record ratings as they come in, and return every agent with their average rating, ordered from highest to lowest.

Ask a few questions first

AskWhat we'll assume
What does a rating look like?A whole number from 1 to 5. Anything else is rejected with an error.
How should ties be ordered?More ratings first, since that average is more trustworthy, then by name so the order is always the same.
How precise should averages be?Round to two decimal places in the output. Keep exact sums inside.
Are writes or reads more common?Lots of ratings come in, and the ranked list is asked for now and then. So recording should be cheap.
What about agents with no ratings?They're not in the list until they get their first one.

A couple of examples

InputOutput
record ana 5, ben 3, ana 4, cy 5, ben 4. Then ranked()[(cy, 5.0), (ana, 4.5), (ben, 3.5)]
record dee 4, eli 5, eli 3, fay 4. Then ranked()[(eli, 4.0), (dee, 4.0), (fay, 4.0)]. All tied, eli has more ratings, then names
record g 1, g 2, g 2. Then ranked()[(g, 1.67)]

Start with brute force

Store every rating in a list. When asked for the ranking, group the list by agent, average each group, and sort.

Cost: recording is O(1), but every ranking call is O(r + a log a) for r ratings and a agents, and you keep every rating forever.

A better way

An average only needs two numbers: the sum and the count. So keep a hash map from agent to (sum, count) and update it on every rating. You never need the full list.

Ranking is then just sorting the agents, which are far fewer than the ratings. Sorting with a key of (negative average, negative count, name) gives highest first, then more ratings, then alphabetical, in one sort.

  1. On record, check the rating is 1 to 5.
  2. Add it to the agent's sum and add one to their count, creating the entry if it's new.
  3. On ranked, build (agent, average, count) for every agent.
  4. Sort by highest average, then most ratings, then name.
  5. Return each agent with their average rounded to two places.

The code

solution.pypython
class AgentRatings:
    def __init__(self):
        self.totals = {}  # agent -> [sum of ratings, number of ratings]

    def record(self, agent, rating):
        if rating not in (1, 2, 3, 4, 5):
            raise ValueError("rating must be a whole number from 1 to 5")
        total = self.totals.setdefault(agent, [0, 0])
        total[0] += rating
        total[1] += 1

    def ranked(self):
        """Every agent with their average, highest first.
        Ties go to the agent with more ratings, then by name."""
        rows = [(agent, s / n, n) for agent, (s, n) in self.totals.items()]
        rows.sort(key=lambda row: (-row[1], -row[2], row[0]))
        return [(agent, round(average, 2)) for agent, average, _ in rows]
CostWhy
RecordO(1)One dictionary update
RankedO(a log a)Sorting a agents
SpaceO(a)Two numbers per agent, no matter how many ratings

Test it

Walk through one of the examples out loud, then check the cases that tend to break things:

  • No ratings yet: return an empty list.
  • Everyone tied: the order falls back to rating count, then name, so it's stable.
  • A rating of 0, 6 or 4.5: rejected with an error.
  • An average like 5/3: shown as 1.67, but the exact sum and count are kept so later ratings stay accurate.

Common mistakes

  • Storing a running average and updating it with rounding each time. Errors build up. Keep the sum and count instead.
  • Sorting by average only, so ties come back in a different order each run.
  • Rounding before sorting, which turns 4.666 and 4.664 into a tie that wasn't there.
  • Dividing with integer division in a language that does that, which floors every average.

Follow-up questions

What if you only want the top 10, with millions of agents?

Use a heap of size 10 while going through the agents. That's O(a log 10) instead of sorting everything. If it's asked for constantly, keep agents in a sorted structure and update one entry per rating.

What if they want averages per month?

Pass a timestamp in with each rating, and key the totals by (agent, month) as well. A month's ranking only looks at that month's entries. For 'last 30 days', keep daily totals and add up the last 30.

How would you export the results?

Add a method that writes ranked() out as CSV rows: agent, average and count. Keep formatting separate from the class that does the math, so adding JSON later doesn't touch it.

How the time adds up

MinutesWhat you're doing
0–5Ask about rating values, ties and rounding
5–10Brute force: keep every rating and group on demand
10–20Keep sums and counts, and pick the sort order
20–35Write the code
35–40Test ties, empty input and bad ratings
40–45Follow-up questions about top k, time periods and export

Key concepts used here