{"id":"code-col-py-loops-to-comprehensions-37","audience":"college","category":"coding","track":"coding","technique":"constraints","title":"Convert two loops to comprehensions on Python 3.7","free":false,"task":"Convert the loops in active_emails and price_map to comprehensions. The lab server runs Python 3.7, so no walrus operator (:=). active_emails must keep order and duplicates (the attendance report counts them). price_map still skips blank prices, and the club treasurer now wants each price rounded to 2 decimals. Keep both docstrings word for word, make each body a single return statement, and leave load_rows alone. Return only the two functions in exactly 1 code block of at most 10 lines, under 20 words of text.","materialLabel":"Your module (attached to your prompt automatically)","material":"# report.py  (Data Club project, runs on the department lab server)\nimport csv\n\n\ndef load_rows(path):\n    with open(path, newline=\"\") as f:\n        return list(csv.DictReader(f))\n\n\ndef active_emails(rows):\n    \"\"\"Lowercased emails of active members, in file order.\"\"\"\n    out = []\n    for row in rows:\n        email = row[\"email\"].strip().lower()\n        if email and row[\"active\"] == \"yes\":\n            out.append(email)\n    return out\n\n\ndef price_map(rows):\n    \"\"\"Map each SKU to its price as a float, skipping blank prices.\"\"\"\n    prices = {}\n    for row in rows:\n        if row[\"price\"].strip():\n            prices[row[\"sku\"]] = float(row[\"price\"])\n    return prices\n\n\n# Used by:\n#   rows = load_rows(\"signups.csv\")\n#   send_reminders(active_emails(rows))\n#   write_invoice(price_map(load_rows(\"merch.csv\")))\n# Runs every Monday from the lab's shared cron.","rules":[{"type":"codeBlocks","count":1},{"type":"maxCodeLines","max":10},{"type":"maxProseWords","max":20}],"mistakes":[{"title":"Using the walrus operator","why":"To avoid stripping twice, the AI reaches for :=, which crashes on Python 3.7.","fix":"Add: \"Python 3.7: no walrus operator.\"","checks":["r0"]},{"title":"Deduping by accident","why":"A set comprehension looks tidy but drops the duplicates the attendance report needs.","fix":"Add: \"Keep order and duplicates: a list, not a set.\"","checks":["r0"]},{"title":"Missing the rounding","why":"The AI only converts what's there; it can't know the treasurer wants 2 decimals.","fix":"Add: \"round(float(row['price']), 2); still skip blank prices.\"","checks":["r1"]},{"title":"Rewriting the module","why":"The AI reprints the file, drops docstrings or refactors load_rows too.","fix":"Add: \"Only these two functions, docstrings unchanged, one return each.\"","checks":["r2","codeBlocks","maxCodeLines","maxProseWords"]}],"promptHints":[{"label":"Names the Python version","patterns":["3\\.7","walrus",":="]},{"label":"Keeps duplicates","patterns":["duplicate","order","not a set"]},{"label":"Adds the rounding","patterns":["round","2 decimal"]},{"label":"Protects the docstrings","patterns":["docstring"]},{"label":"Asks for only two functions","patterns":["only (the )?(two|2) functions","leave load_rows"]}],"hint":"'More pythonic' can quietly change output or use syntax your server can't run. Tell the AI the Python version, what must stay the same and exactly what to hand back.","rubricLabels":["active_emails returns a list comprehension of row[\"email\"].strip().lower() for rows where the stripped email is non-empty and row[\"active\"] == \"yes\", keeping order and duplicates (no set), and no := appears anywhere","price_map returns a dict comprehension {row[\"sku\"]: round(float(row[\"price\"]), 2) ...} that rounds to 2 decimals and skips rows whose price is blank after strip()","Only active_emails and price_map are returned, each with its original docstring unchanged and a body that is a single return statement"]}