Day 11 – List Comprehensions, Lambda, map/filter
⚡ Day 11 – List Comprehensions, Lambda, map/filter
1. Learning Objectives
By the end of Day 11, you will be able to:
- Write concise list comprehensions to create and filter lists in a single line.
- Use dictionary comprehensions and set comprehensions for similar tasks.
- Create anonymous functions with
lambda. - Apply
map()andfilter()for functional‑style data processing. - Choose between comprehensions and
map/filterfor AEC tasks (e.g., filtering beams longer than 6 m, converting all lengths to mm, extracting unique material names).
2. Concept Explanation
2.1 List Comprehensions
A list comprehension provides a compact way to create a list by applying an expression to each item in an iterable, optionally with a filtering condition.
Syntax:
[expression for item in iterable if condition]
Traditional loop vs. comprehension:
# Traditional loop
squares = []
for x in range(10):
squares.append(x**2)
# List comprehension (one line)
squares = [x**2 for x in range(10)]
With condition:
# Only even squares
even_squares = [x**2 for x in range(10) if x % 2 == 0]
Why comprehensions?
- Readable – expresses intent clearly.
- Faster – executes at C speed internally (important for large AEC datasets).
- Pythonic – the preferred style in professional code.
2.2 Dictionary and Set Comprehensions
# Dictionary comprehension: {key_expression: value_expression for item in iterable}
beam_moments = {b["mark"]: b["load"] * b["span"]**2 / 8 for b in beams}
# Set comprehension: {expression for item in iterable}
unique_materials = {b["material"].lower() for b in beams if "material" in b}
2.3 Lambda Functions
A lambda is a small anonymous function defined in one line: lambda arguments: expression
# Regular function
def double(x):
return x * 2
# Lambda equivalent
double = lambda x: x * 2
Use cases:
- Short, throw‑away functions passed to
map(),filter(),sorted(), etc. - Key functions for sorting:
sorted(beams, key=lambda b: b["span"])
Limitations:
- Can only contain a single expression (no statements, loops, or
return). - Overuse reduces readability – if logic is complex, use a
def.
2.4 map() and filter()
map(function, iterable) – applies a function to every item, returns an iterator.
filter(function, iterable) – keeps items where function returns True, returns an iterator.
# map: convert list of strings to floats
span_strings = ["6.0", "4.5", "7.2"]
spans = list(map(float, span_strings)) # [6.0, 4.5, 7.2]
# filter: keep spans > 6.0
long_spans = list(filter(lambda s: s > 6.0, spans)) # [7.2]
Comprehensions vs. map/filter – usually comprehensions are more readable and Pythonic. Use map/filter when you already have a named function (e.g., map(str.strip, lines)).
3. Code Examples
Example 1: Beam span filtering – comprehension vs. loop
all_spans = [6.0, 4.5, 7.2, 5.0, 8.1, 3.5]
# Traditional loop
long_spans_loop = []
for s in all_spans:
if s > 6.0:
long_spans_loop.append(s)
# List comprehension (preferred)
long_spans_comp = [s for s in all_spans if s > 6.0]
print(f"Spans > 6.0m: {long_spans_comp}")
Example 2: Applying unit conversion with map and lambda
# Convert a list of lengths from metres to millimetres
lengths_m = [3.5, 4.0, 6.0, 2.8]
lengths_mm = list(map(lambda x: x * 1000, lengths_m))
print(f"In mm: {lengths_mm}") # [3500.0, 4000.0, 6000.0, 2800.0]
# Using comprehension (cleaner)
lengths_mm2 = [x * 1000 for x in lengths_m]
Example 3: Dictionary of beam moments with comprehension
beams = [
{"mark": "B1", "span": 6.0, "load": 25},
{"mark": "B2", "span": 4.5, "load": 18},
{"mark": "B3", "span": 7.2, "load": 30},
]
# Dictionary comprehension: mark -> max moment (kNm)
moments = {b["mark"]: b["load"] * b["span"]**2 / 8 for b in beams}
print(moments) # {'B1': 112.5, 'B2': 45.5625, 'B3': 194.4}
Example 4: Nested comprehension – column grid coordinates
# Generate all (x,y) coordinates for a 3x4 grid
x_spacing, y_spacing = 6.0, 8.0
columns = [(col * x_spacing, row * y_spacing) for row in range(3) for col in range(4)]
print(columns)
# [(0.0, 0.0), (6.0, 0.0), (12.0, 0.0), (18.0, 0.0),
# (0.0, 8.0), (6.0, 8.0), (12.0, 8.0), (18.0, 8.0),
# (0.0, 16.0), (6.0, 16.0), (12.0, 16.0), (18.0, 16.0)]
Example 5: filter() with lambda for material validation
materials = ["Concrete", "Steel", "Wood", "Aluminium", "Timber"]
valid_materials = {"concrete", "steel", "timber", "masonry"}
# Filter valid materials (case‑insensitive)
approved = list(filter(lambda m: m.lower() in valid_materials, materials))
print(approved) # ['Concrete', 'Steel', 'Timber']
# Same with comprehension
approved2 = [m for m in materials if m.lower() in valid_materials]
4. Hands‑on Exercises (3–5 Problems)
Problem 1 – Filter short spans
Given spans = [6.0, 4.5, 7.2, 3.0, 5.5, 8.1], use a list comprehension to create a list of spans that are less than 5.0 m. Print the result.
Problem 2 – Convert weights
Given a list of beam weights in kg/m: weights_kgpm = [60, 80, 50, 100, 75]
Convert to kN/m (multiply by 0.00980665) using map() and lambda. Print the result.
Problem 3 – Material name cleaning
You have a list of raw material names:raw_materials = [" Concrete ", "STEEL", " timber ", "Aluminium"]
Use a list comprehension to .strip().lower() each item. Print the cleaned list.
Problem 4 – Dictionary of volumes
Given a list of room dimensions as dictionaries:
rooms = [
{"name": "Lobby", "length": 10.0, "width": 8.0, "height": 4.0},
{"name": "Office A", "length": 6.0, "width": 5.0, "height": 3.0},
{"name": "Meeting", "length": 8.0, "width": 6.0, "height": 3.5},
]
Use a dictionary comprehension to create a mapping room_name → volume. Print the result.
Problem 5 – Extract unique floor usages
Given the storeys list from Day 6:
storeys = [
("Level 1", 4.5, "Retail"),
("Level 2", 3.5, "Office"),
("Level 3", 3.5, "Office"),
("Level 4", 3.5, "Office"),
("Level 5", 3.0, "MEP"),
("Level 6", 3.0, "Roof Terrace")
]
Use a set comprehension to extract all unique usages. Print the set.
Solutions (attempt first):
# P1
spans = [6.0, 4.5, 7.2, 3.0, 5.5, 8.1]
short = [s for s in spans if s < 5.0]
print(short) # [4.5, 3.0]
# P2
weights_kgpm = [60, 80, 50, 100, 75]
weights_kNm = list(map(lambda w: w * 0.00980665, weights_kgpm))
print(weights_kNm) # [0.5884, 0.7845, 0.4903, 0.9807, 0.7355]
# P3
raw_materials = [" Concrete ", "STEEL", " timber ", "Aluminium"]
cleaned = [m.strip().lower() for m in raw_materials]
print(cleaned) # ['concrete', 'steel', 'timber', 'aluminium']
# P4
rooms = [
{"name": "Lobby", "length": 10.0, "width": 8.0, "height": 4.0},
{"name": "Office A", "length": 6.0, "width": 5.0, "height": 3.0},
{"name": "Meeting", "length": 8.0, "width": 6.0, "height": 3.5},
]
volumes = {r["name"]: r["length"] * r["width"] * r["height"] for r in rooms}
print(volumes) # {'Lobby': 320.0, 'Office A': 90.0, 'Meeting': 168.0}
# P5
storeys = [
("Level 1", 4.5, "Retail"),
("Level 2", 3.5, "Office"),
("Level 3", 3.5, "Office"),
("Level 4", 3.5, "Office"),
("Level 5", 3.0, "MEP"),
("Level 6", 3.0, "Roof Terrace")
]
unique_usages = {usage for _, _, usage in storeys}
print(unique_usages) # {'Retail', 'Office', 'MEP', 'Roof Terrace'}
5. Applied Challenge Task
Task: Structural Member Data Processor with Comprehensions
You are given a list of steel beam dictionaries:
beams = [
{"mark": "B1", "span": 6.0, "load": 25, "fy": 250},
{"mark": "B2", "span": 4.5, "load": 18, "fy": 250},
{"mark": "B3", "span": 7.2, "load": 30, "fy": 355},
{"mark": "B4", "span": 5.0, "load": 22, "fy": 250},
{"mark": "B5", "span": 8.0, "load": 35, "fy": 355},
]
Write a script that uses comprehensions (list, dict, or set) and lambda/map/filter wherever possible to:
- Create a list of beam marks for beams with
span > 6.0m. - Create a dictionary mapping
mark → max_moment(useM = load * span² / 8). - Compute the required section modulus
S_req = M * 1e6 / (0.6 * fy)(in mm³) for each beam usingmap(). - Filter out beams that would require
S_req > 1_000_000mm³ (too heavy). Print their marks. - Find the total steel weight assuming each beam weighs 80 kg/m. Use
map()andsum(). - Extract the set of unique yield strengths (
fy) used.
Why this matters:
Comprehensions and functional tools let you process entire datasets (hundreds of members) in just a few lines – the hallmark of efficient AEC scripting.
6. Brief Review Summary
- List comprehensions:
[expr for item in iterable if condition]– concise, fast, Pythonic. - Dict comprehensions:
{k: v for item in iterable}– build dictionaries in one line. - Set comprehensions:
{expr for item in iterable}– unique items. - Lambda: single‑expression anonymous function:
lambda args: expr. map(func, iterable)– transform each element.filter(func, iterable)– keep elements where function returnsTrue.- Prefer comprehensions over
map/filterfor readability, unless a named function is already available.
Key takeaway:
These tools let you write expressive, high‑performance data‑processing code. For AEC, they are invaluable when analysing lists of beams, columns, rooms, or materials – turning multi‑line loops into single, clear expressions.
7. Preview of Next Topic (Day 12)
Tomorrow we’ll cover Modules, Packages, and Virtual Environments.
You’ll learn:
- Organising your AEC functions into reusable modules (
.pyfiles). - Creating a package with
__init__.py(e.g.,aec_utilspackage). - Using
importto bring in your own code and third‑party libraries. - Setting up virtual environments to manage dependencies for different projects.
- Practical example: building a package for AEC unit conversions and section properties that you can reuse across projects.
Modular code is the foundation of professional software engineering in AEC.

Comments
Post a Comment