Day 6: List Comprehension & Lambda Functions

Master Python's most elegant functional programming features

List Comprehension Lambda Functional Programming Python Advanced

📚 Table of Contents

🔥 List Comprehensions

List comprehensions provide a concise way to create lists. They're more readable and often faster than traditional for loops for creating lists.

Basic Syntax & Usage

The basic syntax follows the pattern: [expression for item in iterable]

# Traditional approach
squares = []
for i in range(10):
    squares.append(i**2)

# List comprehension approach
squares = [i**2 for i in range(10)]
print(squares)  # [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

# More examples
words = ['hello', 'world', 'python', 'list']
lengths = [len(word) for word in words]
print(lengths)  # [5, 5, 6, 4]

# String operations
names = ['alice', 'bob', 'charlie']
capitalized = [name.capitalize() for name in names]
print(capitalized)  # ['Alice', 'Bob', 'Charlie']

# Mathematical operations
numbers = [1, 2, 3, 4, 5]
doubled = [x * 2 for x in numbers]
cubed = [x**3 for x in numbers]
print(doubled)  # [2, 4, 6, 8, 10]
print(cubed)    # [1, 8, 27, 64, 125]

Conditional Comprehensions

Add filtering and conditional logic to your comprehensions.

Filtering with if condition

# Filter even numbers
numbers = range(10)
evens = [x for x in numbers if x % 2 == 0]
print(evens)  # [0, 2, 4, 6, 8]

# Filter strings by length
words = ['a', 'hello', 'hi', 'python', 'world']
long_words = [word for word in words if len(word) > 3]
print(long_words)  # ['hello', 'python', 'world']

# Multiple conditions
numbers = range(20)
result = [x for x in numbers if x % 2 == 0 if x % 3 == 0]
print(result)  # [0, 6, 12, 18] (divisible by both 2 and 3)

# Alternative: using 'and'
result = [x for x in numbers if x % 2 == 0 and x % 3 == 0]

Conditional expressions (ternary operator)

# Conditional expression in the output
numbers = range(-5, 6)
abs_values = [x if x >= 0 else -x for x in numbers]
print(abs_values)  # [5, 4, 3, 2, 1, 0, 1, 2, 3, 4, 5]

# Categorize numbers
categories = ['positive' if x > 0 else 'negative' if x < 0 else 'zero' 
              for x in numbers]
print(categories)  # ['negative', 'negative', ..., 'zero', 'positive', ...]

# Replace values
data = [1, 2, 0, 4, 0, 6]
cleaned = [x if x != 0 else 'missing' for x in data]
print(cleaned)  # [1, 2, 'missing', 4, 'missing', 6]
Note: The syntax [expression for item in iterable if condition] filters items, while [expression if condition else alternative for item in iterable] applies conditional logic to the expression.

Nested Comprehensions

Handle nested data structures and create complex transformations.

List of Lists

# Flatten a matrix
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
flattened = [num for row in matrix for num in row]
print(flattened)  # [1, 2, 3, 4, 5, 6, 7, 8, 9]

# Create multiplication table
multiplication_table = [[i * j for j in range(1, 4)] for i in range(1, 4)]
print(multiplication_table)  # [[1, 2, 3], [2, 4, 6], [3, 6, 9]]

# Transpose matrix
transposed = [[row[i] for row in matrix] for i in range(len(matrix[0]))]
print(transposed)  # [[1, 4, 7], [2, 5, 8], [3, 6, 9]]

Working with Nested Structures

# Extract specific data from nested dictionaries
students = [
    {'name': 'Alice', 'grades': [85, 90, 78]},
    {'name': 'Bob', 'grades': [92, 88, 95]},
    {'name': 'Charlie', 'grades': [78, 85, 82]}
]

# Get all grades
all_grades = [grade for student in students for grade in student['grades']]
print(all_grades)  # [85, 90, 78, 92, 88, 95, 78, 85, 82]

# Get high grades only
high_grades = [grade for student in students for grade in student['grades'] 
               if grade >= 85]
print(high_grades)  # [85, 90, 92, 88, 95, 85]

# Nested comprehension with conditions
coordinates = [(x, y) for x in range(3) for y in range(3) if x != y]
print(coordinates)  # [(0, 1), (0, 2), (1, 0), (1, 2), (2, 0), (2, 1)]

Generator Expressions vs List Comprehensions

Understanding when to use generator expressions for memory efficiency.

# List comprehension - creates entire list in memory
list_comp = [x**2 for x in range(1000000)]
print(type(list_comp))  # 

# Generator expression - creates generator object
gen_exp = (x**2 for x in range(1000000))
print(type(gen_exp))    # 

# Memory usage comparison
import sys
list_size = sys.getsizeof([x for x in range(1000)])
gen_size = sys.getsizeof((x for x in range(1000)))
print(f"List: {list_size} bytes, Generator: {gen_size} bytes")

# Using generator expressions
def process_large_file():
    # Efficient for large datasets
    return (line.strip().upper() for line in open('file.txt'))

# Convert generator to list when needed
squares_gen = (x**2 for x in range(10))
squares_list = list(squares_gen)
print(squares_list)  # [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

When to Use Each

Use List Comprehensions when:
  • You need to access elements multiple times
  • You need the length of the collection
  • You need indexing capabilities
  • The dataset is relatively small
Use Generator Expressions when:
  • Working with large datasets
  • You only iterate through once
  • Memory efficiency is important
  • You want lazy evaluation

Other Comprehension Types

# Set comprehensions
numbers = [1, 2, 2, 3, 3, 4, 5]
unique_squares = {x**2 for x in numbers}
print(unique_squares)  # {1, 4, 9, 16, 25}

# Dictionary comprehensions
words = ['hello', 'world', 'python']
word_lengths = {word: len(word) for word in words}
print(word_lengths)  # {'hello': 5, 'world': 5, 'python': 6}

# Dictionary comprehension with conditions
numbers = range(10)
even_squares = {x: x**2 for x in numbers if x % 2 == 0}
print(even_squares)  # {0: 0, 2: 4, 4: 16, 6: 36, 8: 64}

🚀 Lambda Functions

Lambda functions are small anonymous functions that can have any number of arguments but can only have one expression.

Lambda Syntax & Use Cases

Basic syntax: lambda arguments: expression

Basic Lambda Examples

# Basic lambda function
square = lambda x: x**2
print(square(5))  # 25

# Multiple arguments
add = lambda x, y: x + y
print(add(3, 7))  # 10

# Lambda with default arguments
greet = lambda name, greeting="Hello": f"{greeting}, {name}!"
print(greet("Alice"))  # Hello, Alice!
print(greet("Bob", "Hi"))  # Hi, Bob!

# Conditional lambda
max_of_two = lambda x, y: x if x > y else y
print(max_of_two(10, 5))  # 10

# Lambda returning lambda (higher-order function)
multiply_by = lambda n: lambda x: x * n
double = multiply_by(2)
triple = multiply_by(3)
print(double(5))  # 10
print(triple(5))  # 15

Common Use Cases

# Sorting with custom key
students = [('Alice', 85), ('Bob', 90), ('Charlie', 78)]
# Sort by grade (second element)
sorted_by_grade = sorted(students, key=lambda x: x[1])
print(sorted_by_grade)  # [('Charlie', 78), ('Alice', 85), ('Bob', 90)]

# Sort by name length
words = ['python', 'java', 'c', 'javascript']
sorted_by_length = sorted(words, key=lambda x: len(x))
print(sorted_by_length)  # ['c', 'java', 'python', 'javascript']

# Complex sorting
data = [{'name': 'Alice', 'age': 25, 'score': 85},
        {'name': 'Bob', 'age': 23, 'score': 90},
        {'name': 'Charlie', 'age': 25, 'score': 78}]

# Sort by age, then by score
sorted_data = sorted(data, key=lambda x: (x['age'], x['score']))
print(sorted_data)

# Event handling (conceptual example)
button_actions = {
    'save': lambda: print("File saved!"),
    'load': lambda: print("File loaded!"),
    'exit': lambda: print("Goodbye!")
}
button_actions['save']()  # File saved!

Lambda Limitations

Lambda Limitations:
  • Can only contain expressions, not statements
  • No annotations or docstrings
  • Limited to single expression
  • Can't contain print, return, pass, assert statements
  • Harder to debug than named functions
# These don't work with lambda:
# lambda x: print(x)  # print is a statement
# lambda x: return x  # return is a statement
# lambda x: pass      # pass is a statement

# These work:
# Use expression equivalents
output = lambda x: x  # Instead of print, return the value
condition = lambda x: x if x > 0 else 0  # Instead of if statement

🔧 Map, Filter & Reduce with Lambdas

Functional programming tools that work beautifully with lambda functions.

Map Function

Applies a function to every item in an iterable.

# Basic map usage
numbers = [1, 2, 3, 4, 5]
squares = list(map(lambda x: x**2, numbers))
print(squares)  # [1, 4, 9, 16, 25]

# Multiple iterables
list1 = [1, 2, 3, 4]
list2 = [10, 20, 30, 40]
products = list(map(lambda x, y: x * y, list1, list2))
print(products)  # [10, 40, 90, 160]

# String operations
words = ['hello', 'world', 'python']
capitalized = list(map(lambda x: x.upper(), words))
print(capitalized)  # ['HELLO', 'WORLD', 'PYTHON']

# Complex transformations
data = ['1,2,3', '4,5,6', '7,8,9']
parsed = list(map(lambda x: [int(i) for i in x.split(',')], data))
print(parsed)  # [[1, 2, 3], [4, 5, 6], [7, 8, 9]]

# Map vs List comprehension
# These are equivalent:
result1 = list(map(lambda x: x**2, range(5)))
result2 = [x**2 for x in range(5)]
print(result1 == result2)  # True

Filter Function

Filters elements from an iterable based on a condition.

# Basic filter usage
numbers = range(10)
evens = list(filter(lambda x: x % 2 == 0, numbers))
print(evens)  # [0, 2, 4, 6, 8]

# Filter strings
words = ['apple', 'banana', 'cherry', 'date', 'elderberry']
long_words = list(filter(lambda x: len(x) > 5, words))
print(long_words)  # ['banana', 'cherry', 'elderberry']

# Filter with complex conditions
students = [
    {'name': 'Alice', 'grade': 85, 'age': 20},
    {'name': 'Bob', 'grade': 92, 'age': 19},
    {'name': 'Charlie', 'grade': 78, 'age': 21},
    {'name': 'Diana', 'grade': 88, 'age': 20}
]

high_achievers = list(filter(lambda s: s['grade'] > 80 and s['age'] < 21, students))
print(high_achievers)  # [{'name': 'Alice', ...}, {'name': 'Bob', ...}]

# Remove None values
mixed_data = [1, None, 2, None, 3, 0, 4]
cleaned = list(filter(lambda x: x is not None, mixed_data))
print(cleaned)  # [1, 2, 3, 0, 4]

# Filter vs List comprehension
# These are equivalent:
result1 = list(filter(lambda x: x > 0, [-2, -1, 0, 1, 2]))
result2 = [x for x in [-2, -1, 0, 1, 2] if x > 0]
print(result1 == result2)  # True

Reduce Function

Applies a rolling computation to sequential pairs of values in a list.

from functools import reduce

# Basic reduce usage - sum
numbers = [1, 2, 3, 4, 5]
total = reduce(lambda x, y: x + y, numbers)
print(total)  # 15

# Product of all numbers
product = reduce(lambda x, y: x * y, numbers)
print(product)  # 120

# Find maximum
numbers = [3, 7, 2, 9, 1, 5]
maximum = reduce(lambda x, y: x if x > y else y, numbers)
print(maximum)  # 9

# String concatenation
words = ['Hello', ' ', 'World', '!']
sentence = reduce(lambda x, y: x + y, words)
print(sentence)  # Hello World!

# With initial value
numbers = [1, 2, 3, 4, 5]
total_with_initial = reduce(lambda x, y: x + y, numbers, 10)
print(total_with_initial)  # 25 (10 + 1 + 2 + 3 + 4 + 5)

# Complex reduce operations
transactions = [100, -50, 200, -75, 150]
final_balance = reduce(lambda balance, transaction: balance + transaction, 
                      transactions, 0)
print(final_balance)  # 325

Chaining Map, Filter, and Reduce

# Process data through multiple stages
data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

# Filter evens, square them, then sum
result = reduce(lambda x, y: x + y,
               map(lambda x: x**2,
                  filter(lambda x: x % 2 == 0, data)))
print(result)  # 220 (2² + 4² + 6² + 8² + 10² = 4 + 16 + 36 + 64 + 100)

# More readable version using variables
evens = filter(lambda x: x % 2 == 0, data)
squares = map(lambda x: x**2, evens)
total = reduce(lambda x, y: x + y, squares)
print(total)  # 220

🔗 Combining Comprehensions & Lambdas

Powerful patterns that combine the elegance of both features.

Lambdas within Comprehensions

# Using lambda in list comprehensions
numbers = [1, 2, 3, 4, 5]
operations = [lambda x: x**2, lambda x: x**3, lambda x: x*2]

# Apply each operation to each number
results = [[op(num) for op in operations] for num in numbers]
print(results)  # [[1, 1, 2], [4, 8, 4], [9, 27, 6], [16, 64, 8], [25, 125, 10]]

# Create function mapping
func_map = {f'op{i}': op for i, op in enumerate(operations)}
applied = [func_map['op0'](x) for x in numbers]  # Apply first operation
print(applied)  # [1, 4, 9, 16, 25]

# Dynamic function creation
multipliers = [lambda x, n=i: x * n for i in range(1, 6)]
results = [mult(10) for mult in multipliers]
print(results)  # [10, 20, 30, 40, 50]

Comprehensions with Map/Filter/Reduce

# List comprehension equivalent to map + filter
data = range(10)
# Using map and filter
result1 = list(map(lambda x: x**2, filter(lambda x: x % 2 == 0, data)))
# Using list comprehension
result2 = [x**2 for x in data if x % 2 == 0]
print(result1 == result2)  # True

# Complex data processing
students = [
    {'name': 'Alice', 'scores': [85, 90, 78]},
    {'name': 'Bob', 'scores': [92, 88, 95]},
    {'name': 'Charlie', 'scores': [78, 85, 82]}
]

# Calculate average scores using comprehension + lambda
averages = [(student['name'], 
            sum(student['scores']) / len(student['scores']))
           for student in students]
print(averages)  # [('Alice', 84.33), ('Bob', 91.67), ('Charlie', 81.67)]

# Using map with comprehension results
words = ['hello', 'world', 'python']
word_data = [{'word': word, 'length': len(word), 'upper': word.upper()} 
             for word in words]
lengths = list(map(lambda x: x['length'], word_data))
print(lengths)  # [5, 5, 6]

Advanced Combinations

# Matrix operations with comprehensions and lambdas
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]

# Apply function to each element
apply_func = lambda x: x**2
squared_matrix = [[apply_func(cell) for cell in row] for row in matrix]
print(squared_matrix)  # [[1, 4, 9], [16, 25, 36], [49, 64, 81]]

# Conditional operations
transform = lambda x: x * 2 if x % 2 == 0 else x * 3
transformed = [[transform(cell) for cell in row] for row in matrix]
print(transformed)  # [[3, 4, 9], [8, 15, 12], [21, 16, 27]]

# Functional pipeline
def process_data(data, *functions):
    """Apply a series of functions to data"""
    result = data
    for func in functions:
        result = func(result)
    return result

# Example usage
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
result = process_data(
    numbers,
    lambda x: filter(lambda n: n % 2 == 0, x),  # Filter evens
    lambda x: map(lambda n: n**2, x),           # Square them
    lambda x: [n for n in x if n > 10],         # Filter > 10
    list                                        # Convert to list
)
print(result)  # [16, 36, 64, 100]

⚡ Performance Notes

Understanding the performance characteristics of comprehensions and lambdas.

Performance Comparisons

import time

# Performance test function
def time_function(func, *args, iterations=1000000):
    start = time.time()
    for _ in range(iterations):
        func(*args)
    end = time.time()
    return end - start

# Test data
numbers = list(range(100))

# List comprehension vs for loop
def for_loop_squares(nums):
    result = []
    for num in nums:
        result.append(num**2)
    return result

def list_comp_squares(nums):
    return [num**2 for num in nums]

# List comprehension vs map
def map_squares(nums):
    return list(map(lambda x: x**2, nums))

# Performance results (approximate)
print("For loop:", time_function(for_loop_squares, numbers))
print("List comp:", time_function(list_comp_squares, numbers))
print("Map + lambda:", time_function(map_squares, numbers))

Memory Usage

import sys

# Memory comparison
def memory_usage_example():
    n = 10000
    
    # List comprehension
    list_comp = [x**2 for x in range(n)]
    list_size = sys.getsizeof(list_comp)
    
    # Generator expression
    gen_exp = (x**2 for x in range(n))
    gen_size = sys.getsizeof(gen_exp)
    
    print(f"List comprehension: {list_size} bytes")
    print(f"Generator expression: {gen_size} bytes")
    print(f"Ratio: {list_size / gen_size:.1f}x larger")

memory_usage_example()
Performance Tips:
  • List comprehensions are generally faster than equivalent for loops
  • Generator expressions use constant memory regardless of input size
  • Built-in functions (map, filter) can be faster for simple operations
  • Avoid complex lambdas in performance-critical code
  • Use appropriate data structure (list vs generator) based on usage pattern

⚠️ Common Pitfalls & Best Practices

Late Binding Trap

The most common pitfall when using lambdas in loops.

# WRONG: Late binding trap
functions = []
for i in range(5):
    functions.append(lambda: i)  # All lambdas capture the same 'i'

# All functions return 4 (the final value of i)
for func in functions:
    print(func())  # 4, 4, 4, 4, 4

# CORRECT: Early binding with default parameter
functions = []
for i in range(5):
    functions.append(lambda x=i: x)  # Capture current value of i

for func in functions:
    print(func())  # 0, 1, 2, 3, 4

# ALTERNATIVE: Using list comprehension (recommended)
functions = [lambda x=i: x for i in range(5)]
for func in functions:
    print(func())  # 0, 1, 2, 3, 4

Readability Issues

# AVOID: Complex nested comprehensions
result = [[func(x) for func in [lambda n: n**2, lambda n: n**3]] 
          for x in range(5) if x % 2 == 0]

# BETTER: Break it down
operations = [lambda n: n**2, lambda n: n**3]
even_numbers = [x for x in range(5) if x % 2 == 0]
result = [[func(x) for func in operations] for x in even_numbers]

# EVEN BETTER: Use named functions
def square(n): return n**2
def cube(n): return n**3

operations = [square, cube]
result = [[func(x) for func in operations] for x in even_numbers]

Best Practices

List Comprehension Best Practices:
  • Keep comprehensions simple and readable
  • Limit to 2-3 levels of nesting maximum
  • Use meaningful variable names
  • Consider breaking complex comprehensions into multiple steps
  • Use generator expressions for large datasets
Lambda Best Practices:
  • Use lambdas for simple, one-line functions
  • Prefer named functions for complex logic
  • Be careful with variable capture in loops
  • Don't assign lambdas to variables (use def instead)
  • Use lambdas primarily with map, filter, sort, etc.

When NOT to Use Comprehensions/Lambdas

# AVOID: Side effects in comprehensions
# BAD
[print(x) for x in range(5)]  # Don't use comprehensions for side effects

# GOOD
for x in range(5):
    print(x)

# AVOID: Complex logic
# BAD
result = [x if x > 0 else -x if x < 0 else 0 for x in numbers]

# GOOD
def absolute_value(x):
    if x > 0:
        return x
    elif x < 0:
        return -x
    else:
        return 0

result = [absolute_value(x) for x in numbers]

🎯 Interview Questions & Practice Problems

Problem 1: Flatten Nested Lists

Flatten a list of lists using list comprehension.

# Input
nested = [[1, 2], [3, 4, 5], [6]]
# Output: [1, 2, 3, 4, 5, 6]
# Solution
flattened = [item for sublist in nested for item in sublist]

Problem 2: Prime Numbers Filter

Find all prime numbers in a range using filter and lambda.

# Input: numbers 2 to 20
# Output: [2, 3, 5, 7, 11, 13, 17, 19]
# Solution
is_prime = lambda n: n > 1 and all(n % i != 0 for i in range(2, int(n**0.5) + 1))
primes = list(filter(is_prime, range(2, 21)))

Problem 3: Dictionary from Lists

Create a dictionary from two lists using comprehension.

# Input
keys = ['a', 'b', 'c']
values = [1, 2, 3]
# Output: {'a': 1, 'b': 2, 'c': 3}
# Solution
result = {k: v for k, v in zip(keys, values)}

Problem 4: Word Frequency Counter

Count word frequencies using comprehension and reduce.

# Input
text = "hello world hello python world"
# Output: {'hello': 2, 'world': 2, 'python': 1}
# Solution
from functools import reduce
words = text.split()
freq = reduce(lambda acc, word: {**acc, word: acc.get(word, 0) + 1}, words, {})

Problem 5: Matrix Transpose

Transpose a matrix using list comprehension.

# Input
matrix = [[1, 2, 3], [4, 5, 6]]
# Output: [[1, 4], [2, 5], [3, 6]]
# Solution
transposed = [[row[i] for row in matrix] for i in range(len(matrix[0]))]

Problem 6: Custom Sort with Lambda

Sort students by grade descending, then by name ascending.

# Input
students = [('Alice', 85), ('Bob', 90), ('Charlie', 85)]
# Output: [('Bob', 90), ('Alice', 85), ('Charlie', 85)]
# Solution
sorted_students = sorted(students, key=lambda x: (-x[1], x[0]))

Advanced Interview Questions

Problem 7: Fibonacci with Generators

Generate Fibonacci sequence using generator expression and lambda.

# Create first 10 Fibonacci numbers
def fibonacci_gen():
    a, b = 0, 1
    while True:
        yield a
        a, b = b, a + b

fib_gen = fibonacci_gen()
first_10 = [next(fib_gen) for _ in range(10)]
print(first_10)  # [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]

Problem 8: Complex Data Processing

Process nested JSON-like data structure.

# Extract all email addresses from nested user data
users = [
    {'name': 'Alice', 'contacts': [{'type': 'email', 'value': 'alice@email.com'}]},
    {'name': 'Bob', 'contacts': [{'type': 'phone', 'value': '123'}, {'type': 'email', 'value': 'bob@email.com'}]}
]

# Solution
emails = [contact['value'] for user in users 
          for contact in user['contacts'] 
          if contact['type'] == 'email']
print(emails)  # ['alice@email.com', 'bob@email.com']

📋 Quick Reference Cheat Sheet

List Comprehension Syntax

# Basic: [expression for item in iterable]
squares = [x**2 for x in range(10)]

# With condition: [expression for item in iterable if condition]
evens = [x for x in range(10) if x % 2 == 0]

# With conditional expression: [expr1 if condition else expr2 for item in iterable]
signs = ['positive' if x > 0 else 'negative' for x in numbers]

# Nested: [expression for item1 in iterable1 for item2 in iterable2]
pairs = [(x, y) for x in range(3) for y in range(3)]

Lambda Syntax

# Basic: lambda arguments: expression
add = lambda x, y: x + y

# With default args: lambda x, y=default: expression
greet = lambda name, msg="Hello": f"{msg}, {name}"

# Higher-order: lambda that returns lambda
multiply_by = lambda n: lambda x: x * n

Map, Filter, Reduce

# Map: apply function to all items
squared = list(map(lambda x: x**2, [1, 2, 3, 4]))

# Filter: select items that match condition
evens = list(filter(lambda x: x % 2 == 0, range(10)))

# Reduce: accumulate values (need to import)
from functools import reduce
total = reduce(lambda x, y: x + y, [1, 2, 3, 4, 5])

Other Comprehensions

# Set comprehension
unique_squares = {x**2 for x in [1, 2, 2, 3, 3]}

# Dictionary comprehension
word_lengths = {word: len(word) for word in ['hello', 'world']}

# Generator expression (memory efficient)
gen = (x**2 for x in range(1000000))

Common Patterns

# Flatten nested list
flat = [item for sublist in nested for item in sublist]

# Matrix transpose
transposed = [[row[i] for row in matrix] for i in range(len(matrix[0]))]

# Dictionary inversion
inverted = {v: k for k, v in original.items()}

# Conditional filtering
filtered = [x for x in data if condition(x)]

Performance Tips

# Use generator for large data
gen = (process(x) for x in huge_list)

# Chain operations efficiently
result = [transform(x) for x in data if filter_condition(x)]

# Avoid side effects in comprehensions
# DON'T: [print(x) for x in data]
# DO: for x in data: print(x)