Skip to content

S3Q1 · Filter Customers by Spending Pattern

⚡ Quick Reference

Function: filter_customers(data: dict, command: str) -> set

Core idea: For each customer, compute avg / min / max, then test the condition for the requested command.

def avg(L): return sum(L) / len(L)

def filter_customers(data, command):
    result = set()
    for name, spending in data.items():
        a, mn, mx = avg(spending), min(spending), max(spending)
        if   command == 'high_spender'       and a >= 50000:         result.add(name)
        elif command == 'moderate_spender'   and 20000 <= a < 50000: result.add(name)
        elif command == 'consistent_spender' and mn >= 10000:        result.add(name)
        elif command == 'stable_spending'    and mx - mn <= 5000:    result.add(name)
    return result

Key rules: - high_spender: avg >= 50000 - moderate_spender: 20000 <= avg < 50000 - consistent_spender: every month >= 10000, so min >= 10000 - stable_spending: max - min <= 5000


Problem Statement

Problem

Given a dictionary data mapping customer names to lists of monthly spending amounts, return a set of customer names matching the given command:

  • 'high_spender' -- average >= 50000
  • 'moderate_spender' -- average in [20000, 50000)
  • 'consistent_spender' -- every month >= 10000
  • 'stable_spending' -- max - min <= 5000

Similar problems

This follows the same four-category filter pattern as S3Q1 of 2026 May Set 1 (Workout Data Analytics) and S3Q1 of 2026 May Set 2 (Filter Houses). The structure is identical -- only the domain and thresholds differ.

Example:

data = {
    "Akhil":  [45000, 55000, 50000],
    "Bina":   [12000, 18000, 15000],
    "Charan": [70000, 65000, 72000],
    "Divya":  [25000, 30000, 28000],
    "Esha":   [15000, 16000, 15500]
}
Call
filter_customers(data, 'high_spender')
Output
{'Akhil', 'Charan'}
Call
filter_customers(data, 'stable_spending')
Output
{'Divya', 'Esha'}

Understanding the problem

Statistic Used by
avg = sum / len high_spender, moderate_spender
min(spending) consistent_spender
max - min stable_spending

Full trace:

Customer Avg Min Spread HS MS CS SS
Akhil 50000 45000 10000 ✅ ✅
Bina 15000 12000 6000 ✅
Charan 69000 65000 7000 ✅ ✅
Divya 27667 25000 5000 ✅ ✅ ✅
Esha 15500 15000 1000 ✅ ✅

Akhil is high_spender, not moderate_spender

Akhil's average is exactly 50000, which meets avg >= 50000 (high) but fails avg < 50000 (moderate). The boundary belongs to high_spender. Read the inequality direction carefully in each problem.

Divya's spread is exactly 5000 -- qualifies for stable_spending

max - min = 30000 - 25000 = 5000 <= 5000 ✅. The condition is <=, so the boundary value qualifies.


Solution approaches

def avg(L): return sum(L) / len(L)

def filter_customers(data: dict, command: str) -> set:
    result = set()
    for name, spending in data.items():
        a       = avg(spending)
        minimum = min(spending)
        maximum = max(spending)
        spread  = maximum - minimum

        if command == 'high_spender':
            if a >= 50000:
                result.add(name)
        elif command == 'moderate_spender':
            if 20000 <= a < 50000:
                result.add(name)
        elif command == 'consistent_spender':
            if minimum >= 10000:    # min >= 10000 means all months >= 10000
                result.add(name)
        elif command == 'stable_spending':
            if spread <= 5000:
                result.add(name)
    return result
def avg(L): return sum(L) / len(L)

def filter_customers(data: dict, command: str) -> set:
    conditions = {
        'high_spender':       lambda s: avg(s) >= 50000,
        'moderate_spender':   lambda s: 20000 <= avg(s) < 50000,
        'consistent_spender': lambda s: min(s) >= 10000,
        'stable_spending':    lambda s: max(s) - min(s) <= 5000,
    }
    test = conditions[command]
    return {name for name, spending in data.items() if test(spending)}
def filter_customers(data: dict, command: str) -> set:
    avg  = lambda s: sum(s) / len(s)
    test = {
        'high_spender':       lambda s: avg(s) >= 50000,
        'moderate_spender':   lambda s: 20000 <= avg(s) < 50000,
        'consistent_spender': lambda s: min(s) >= 10000,
        'stable_spending':    lambda s: max(s) - min(s) <= 5000,
    }[command]
    return {n for n, s in data.items() if test(s)}

Key takeaways

01

Helper function avg() keeps code DRY

The template provides avg(L) as a helper. Using it avoids repeating sum(L)/len(L) in every branch. When the OPPE template includes a helper, use it -- it signals the intended approach and keeps the solution readable.

02

min() for "all elements above threshold"

min(spending) >= 10000 is equivalent to all(x >= 10000 for x in spending). If the smallest value passes the threshold, all values do. It's O(n) and more concise than an explicit loop.

03

Dict of lambdas scales to new categories

Adding a fifth category is one new line in the dict -- no new elif branch to insert. This pattern separates the rule definitions from the iteration logic, making the code easier to extend and test independently.