1. Lighthouse

Level 4 · Sort products with a key

The request

From Thomas Whitaker, Head of Merchandising

“For the homepage 'premium picks' strip I need these products from most to least expensive. Where two cost the same, put them in alphabetical order so the layout is stable.”

Your goal

Set result to a list of product names sorted by price from highest to lowest; products with the same price are ordered by name A to Z.

  • Store a list of 7 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

    sorted and sort

    sorted() returns a new sorted list and leaves the original alone:

    sorted([189.0, 34.0, 1249.0])                 # [34.0, 189.0, 1249.0]
    sorted([189.0, 34.0, 1249.0], reverse=True)   # [1249.0, 189.0, 34.0]

    Lists also have a .sort() method that sorts in place and returns None. Prefer sorted() when you want to keep the original order around, which is most of the time in analysis.

    Sorting by part of each item

    How do you sort a list of dictionaries? Python can’t compare two dictionaries directly. You tell it what to compare with a key function: a function that receives one item and returns the value to sort by.

    def by_price(p):
        return p["price"]
    
    sorted(products, key=by_price)     # cheapest first

    Note key=by_price without brackets. You’re passing the function, and sorted calls it for each product.

    lambda: a function without a name

    For one-off keys, writing a full def is overkill. A lambda is a small anonymous function written inline:

    sorted(products, key=lambda p: p["price"])

    lambda p: p["price"] means “a function that takes p and returns p["price"]”. It’s limited to a single expression, which is exactly right for keys.

    Sorting on two levels

    To sort by price and then by name, return a tuple from the key. Python compares tuples element by element: first the price, and only if the prices are equal, the name.

    sorted(products, key=lambda p: (p["price"], p["name"]))

    But you want the price high to low and the name A to Z. reverse=True would flip both. The standard trick for numbers is to negate the one you want descending:

    sorted(products, key=lambda p: (-p["price"], p["name"]))

    -1249.0 is smaller than -489.0, so the most expensive product comes first, while names still sort normally. For two text columns in opposite directions, you’d sort twice instead (Python’s sort is stable, meaning equal items keep their existing order), but numbers are by far the common case.

    Why tie-breaking matters

    Without a tie-breaker, products with the same price stay in whatever order they arrived. That order can change between exports, so a homepage, a report or a test that compares lists suddenly “changes” without any real change in the data. Whenever you sort for display or for a top-N list, add a tie-breaker that makes the order fully determined. In interviews, mentioning this unprompted is a good sign.

    Common mistakes

    • Using products.sort() and then assigning its return value, which is None.
    • Writing key=lambda p: p["price"], reverse=True and getting names on ties in Z to A order.
    • Forgetting that you need names, not whole dictionaries, in the final list.

    A look ahead

    In pandas, the same request is products.sort_values(["unit_price", "name"], ascending=[False, True]), with a separate direction for each column.