1. Lighthouse

Level 5 · Filter products in one line

The request

From Thomas Whitaker, Head of Merchandising

“For the 'Light up your evenings' email I need the names of our lighting products under $100. Keep them in the order they appear in the catalog.”

Your goal

Set result to a list of the name values of products whose category is 'Lighting' and whose price is below 100, in the same order as in products.

  • Store a list of 3 in resultnot done yet

Your code

Python

Loading the editor…

    ResultPress Run to see yours

    Mote checks your result

    Press Run. Each row that matches the goal becomes a firefly in your lantern.

    The lesson

    A list of dictionaries is a table

    Look at products: a list where every item is a dictionary with the same keys. That’s a table in disguise. Each dictionary is a row, and each key is a column. It’s also exactly the shape you get from most web APIs and JSON files, so you’ll see it often before the data ever reaches pandas.

    The loop way

    You already know how to filter with a loop and an accumulator:

    names = []
    for p in products:
        if p["category"] == "Lighting" and p["price"] < 100:
            names.append(p["name"])

    Four lines, and the real intent (keep the names of cheap lighting) is spread across all of them.

    The comprehension way

    A list comprehension says the same thing in one expression:

    names = [p["name"] for p in products if p["category"] == "Lighting" and p["price"] < 100]

    Read it in three parts:

    1. p["name"]: what goes into the new list.
    2. for p in products: where the items come from.
    3. if ...: which items to keep (optional).

    The result is always a new list. The order is preserved, because it walks products from start to finish. For long conditions, splitting across lines inside the brackets is perfectly fine and easier to read.

    Transforming as well as filtering

    The first part can be any expression, so comprehensions also reshape data:

    [p["price"] * 0.8 for p in products]                 # every price with 20% off
    [p["name"].upper() for p in products]                # names in capitals
    [round(p["price"] * 1.08, 2) for p in products if p["category"] == "Kitchen"]

    There are dictionary comprehensions too, which build a lookup in one go:

    price_by_name = {p["name"]: p["price"] for p in products}
    price_by_name["Oak Side Table"]    # 149.0

    Watch the boundary

    “Under $100” means strictly below, so < 100. The Edison Bulb Pack at exactly 100.00 must be left out, and the Glass Wall Sconce at 99.99 must stay in. When a brief says “up to” or “at most”, that becomes <=. Read the wording carefully; edge values are exactly what reviewers check.

    When not to use one

    Comprehensions are for building a list. If you need several steps per item, error handling, or you’re not collecting results at all (just printing, say), a normal loop is clearer. A comprehension that needs a scroll bar to read is a sign to go back to a loop.

    Common mistakes

    • Putting the if before the for. [p["name"] if ... for p in products] is a different construct (an inline if-else) and fails without an else.
    • Comparing the category with different casing, like "lighting", which matches nothing.
    • Writing p.name instead of p["name"]. Dictionary values are read with square brackets.

    A look ahead

    In pandas this whole level becomes products.loc[(products["category"] == "Lighting") & (products["unit_price"] < 100), "name"]. Same idea, applied to a whole column at once.