Skip to content

S3Q1 · Student Score Filter

⚡ Quick Reference

Function: filter_students(data: dict, criteria: str) -> set

Four criteria, one dispatcher:

def filter_students(data: dict, criteria: str) -> set:
    if criteria == "excellent":
        return {name for name, scores in data.items()
                if sum(scores) / len(scores) >= 85}
    elif criteria == "good":
        return {name for name, scores in data.items()
                if 50 <= sum(scores) / len(scores) < 85}
    elif criteria == "all_pass":
        return {name for name, scores in data.items()
                if all(s >= 50 for s in scores)}
    elif criteria == "balanced":
        return {name for name, scores in data.items()
                if max(scores) - min(scores) <= 10}

Key rules: - Returns a set of student names - excellent: average >= 85 - good: 50 <= average < 85 - all_pass: every score >= 50 - use all() - balanced: max - min <= 10


Problem Statement

Problem

Write a function filter_students(data: dict, criteria: str) -> set that filters student names from a dictionary based on the given criteria.

Criteria Condition
"excellent" Average score >= 85
"good" Average score >= 50 and < 85
"all_pass" All individual scores >= 50
"balanced" max score - min score <= 10

Example data:

data = {
    "Alice":   [90, 80, 85],
    "Bob":     [40, 50, 60],
    "Charlie": [30, 40, 20],
    "Ram":     [78, 92, 85, 79, 81],
    "Babu":    [67, 70, 75],
    "Kumar":   [100, 100, 100, 100, 100, 100, 40]
}

Expected outputs:

criteria
"excellent"
Output
{'Alice', 'Kumar'}
criteria
"good"
Output
{'Babu', 'Bob', 'Ram'}
criteria
"all_pass"
Output
{'Alice', 'Babu', 'Ram'}
criteria
"balanced"
Output
{'Alice', 'Babu'}

Breaking down each criteria

excellent - average >= 85

avg = sum(scores) / len(scores)
avg >= 85
Student Scores Average >= 85?
Alice [90, 80, 85] 85.0
Bob [40, 50, 60] 50.0
Charlie [30, 40, 20] 30.0
Ram [78, 92, 85, 79, 81] 83.0
Babu [67, 70, 75] 70.67
Kumar [100×6, 40] 91.43

Result: {'Alice', 'Kumar'}


good - 50 <= average < 85

Same average calculation, different bounds. Note: good and excellent are mutually exclusive - a student can't be in both.

Student Average 50 <= avg < 85?
Bob 50.0
Ram 83.0
Babu 70.67

Result: {'Bob', 'Ram', 'Babu'}


all_pass - every score >= 50

One failing score disqualifies. Use all():

all(s >= 50 for s in scores)
Student Scores Any score < 50? all_pass?
Alice [90, 80, 85] No
Bob [40, 50, 60] Yes (40)
Charlie [30, 40, 20] Yes
Ram [78, 92, 85, 79, 81] No
Babu [67, 70, 75] No
Kumar [100×6, 40] Yes (40)

Result: {'Alice', 'Ram', 'Babu'}

all() short-circuits

all(condition for x in iterable) returns False the moment it finds one element that fails the condition - it doesn't check the rest. This makes it efficient for large score lists.


balanced - max - min <= 10

max(scores) - min(scores) <= 10
Student Max Min Difference <= 10?
Alice 90 80 10
Bob 60 40 20
Charlie 40 20 20
Ram 92 78 14
Babu 75 67 8
Kumar 100 40 60

Result: {'Alice', 'Babu'}


Solution approaches

def filter_students(data: dict, criteria: str) -> set:
    if criteria == "excellent":
        return {name for name, scores in data.items()
                if sum(scores) / len(scores) >= 85}
    elif criteria == "good":
        return {name for name, scores in data.items()
                if 50 <= sum(scores) / len(scores) < 85}
    elif criteria == "all_pass":
        return {name for name, scores in data.items()
                if all(s >= 50 for s in scores)}
    elif criteria == "balanced":
        return {name for name, scores in data.items()
                if max(scores) - min(scores) <= 10}

A set comprehension per criteria - clean, readable, and directly returns the required set type.

def filter_students(data: dict, criteria: str) -> set:
    result = set()
    for name, scores in data.items():
        avg = sum(scores) / len(scores)
        if criteria == "excellent":
            if avg >= 85:
                result.add(name)
        elif criteria == "good":
            if 50 <= avg < 85:
                result.add(name)
        elif criteria == "all_pass":
            if all(s >= 50 for s in scores):
                result.add(name)
        elif criteria == "balanced":
            if max(scores) - min(scores) <= 10:
                result.add(name)
    return result

One loop through all students, check the criteria inside. The average is computed once per student regardless of criteria.

def is_excellent(scores):
    return sum(scores) / len(scores) >= 85

def is_good(scores):
    return 50 <= sum(scores) / len(scores) < 85

def is_all_pass(scores):
    return all(s >= 50 for s in scores)

def is_balanced(scores):
    return max(scores) - min(scores) <= 10

def filter_students(data: dict, criteria: str) -> set:
    checks = {
        "excellent": is_excellent,
        "good":      is_good,
        "all_pass":  is_all_pass,
        "balanced":  is_balanced,
    }
    check = checks[criteria]
    return {name for name, scores in data.items() if check(scores)}

Each condition is a named helper function. The dispatch dictionary maps criteria strings to the right function. The most extensible design - adding a new criteria is just adding one helper and one dict entry.

def filter_students(data: dict, criteria: str) -> set:
    conditions = {
        "excellent": lambda scores: sum(scores) / len(scores) >= 85,
        "good":      lambda scores: 50 <= sum(scores) / len(scores) < 85,
        "all_pass":  lambda scores: all(s >= 50 for s in scores),
        "balanced":  lambda scores: max(scores) - min(scores) <= 10,
    }
    check = conditions[criteria]
    return set(
        name for name, scores in data.items() if check(scores)
    )

Each criteria condition is expressed as a lambda stored directly in the dispatch dictionary - no need for separately defined helper functions. filter selects matching names, and set() wraps the result. Compact and all logic lives in one place.


Key takeaways

01

all() for "every element must pass"

all(cond for x in lst) returns True only if every element satisfies the condition. Short-circuits on the first failure - efficient for large lists.

02

Chained comparison for ranges

50 <= avg < 85 is cleaner than avg >= 50 and avg < 85. Python supports chained comparisons natively.

03

Set comprehension {x for x in ...}

Like a list comprehension but returns a set. Use it when the problem explicitly asks for a set, or when the result must have no duplicates.