VPython MapReduceFilter
Sabrina Yang - Spring 2026
Introduction
In Python, map(), filter(), and reduce() are
known as higher-order functions. Higher-order functions are functions that take other functions as arguments.
These functions are especially valuable.
A typical simulation can include hundreds of objects and variables that each operate under the same physics laws. Instead of writing
for loops to iterate through each object, map(), filter(),
and reduce() handles the iteration automatically, which makes the code shorter and more efficient.
The three functions each serve a its own role:
map(): transforms every element in a list by applying a function to itfilter(): selects a subset of elements that satisfy a conditionreduce(): aggregates all elements into a single accumulated value
Important note for Python 3: map() and filter() return
lazy iterator objects, in other words objects that don't do any work until you ask it for the results, rather than lists. This means they do not compute anything until
you actually need the values. Wrap them in list() to force evaluation and
get a plain list back. reduce() was removed from Python 3's built-ins and
must be explicitly imported:
from functools import reduce
Important note for GlowScript VPython: GlowScript runs a restricted subset of
Python and does not support lambda expressions or the functools module.
Use named def functions instead of lambdas, and implement
reduce() manually as shown in the interactive simulation section below.
Background: Functional Programming
To fully understand map(), filter(), and reduce(),
you need to understand first-class functions. In Python, all functions
are first class objects, meaning they can be:
- Assigned to variables
- Passed as arguments to other functions
- Returned as values from other functions
This is what makes higher-order functions possible. When you write
map(calc_weight, masses), you are passing the function
calc_weight itself as an argument to
map(). map() then calls calc_weight
for each element.
Lambda Expressions
A lambda expression is a function you write in one line without giving it a name. The syntax is:
lambda <parameters>: <expression>
The function exists only at the point where it is used. These two definitions produce the same results:
# Traditional way
def square(x):
return x**2
# Using lambda
square = lambda x: x**2
Lambdas save you from having to define a whole separate function when you only need it once:
# Without lambda: requires a function defined somewhere else result = list(map(square, [1, 2, 3, 4])) # With lambda: no defined function needed result = list(map(lambda x: x**2, [1, 2, 3, 4])) # Result: [1, 4, 9, 16]
Lambda expressions can also take multiple parameters, which is useful for
reduce():
from functools import reduce total = reduce(lambda acc, x: acc + x, [1, 2, 3, 4], 0) # Result: 10
When to use lambdas vs def:
- Use a lambda when the function is short, used only once, and
passed directly as an argument
- Use
defwhen the function is longer than one line or reused constantly
GlowScript limitation: Lambda expressions are not supported in GlowScript VPython.
Always use named def functions when writing code for Trinket or
GlowScript.
Inputs and Type Matching
All three functions share the same basic calling convention:
higher_order_function(function, iterable)
The function argument describes the operation to perform. The iterable argument is the sequence of data to operate on, which can be a list, tuple, range, or any other iterable Python object.
One important constraint is type matching: the function must be able to accept
the types contained in the iterable. For example, if your list contains floating-point
numbers, your function must expect floats. Mismatches will cause a TypeError
at runtime.
Example using a named function:
def cubed(x):
return x**3
items = [1, 2, 3, 4]
result = list(map(cubed, items))
# Result: [1, 8, 27, 64]
The same using a lambda:
items = [1, 2, 3, 4] result = list(map(lambda x: x**3, items)) # Result: [1, 8, 27, 64]
You can also pass Python's built-in functions directly:
words = ['hello', 'world', 'vpython'] lengths = list(map(len, words)) # Result: [5, 5, 7]
Map()
How it works
map(function, iterable) applies a function to every element of an
iterable and returns an iterator of the results. The original list is never modified. Instead,
a new sequence of transformed values is produced. The output always has the same number
of elements as the input.
map(function, iterable)
For a list [a, b, c, d] and a function f:
map(f, [a, b, c, d]) → [f(a), f(b), f(c), f(d)]
Basic example
numlist = [1, 2, 3, 4, 5] result = list(map(lambda x: x * 2, numlist)) # Result: [2, 4, 6, 8, 10]
Examples using Physics
Computing gravitational weight (F = mg) for a set of masses:
g = 9.8 # m/s^2 masses = [0.5, 1.0, 2.5, 5.0, 10.0] # kg weights = list(map(lambda m: m * g, masses)) # Result: [4.9, 9.8, 24.5, 49.0, 98.0] Newtons
Computing kinetic energy (KE = ½mv²) for a set of velocities:
mass = 2.0 # kg velocities = [3.0, 5.5, 2.1, 8.0] # m/s ke_list = list(map(lambda v: 0.5 * mass * v**2, velocities)) # Result: [9.0, 30.25, 4.41, 64.0] Joules
Converting a list of temperatures from Celsius to Kelvin:
temps_C = [0, 20, 37, 100, -273.15] temps_K = list(map(lambda T: T + 273.15, temps_C)) # Result: [273.15, 293.15, 310.15, 373.15, 0.0] Kelvin
Why use map() instead of a for-loop?
Both of the following produce the same result, but map() is shorter, and expresses the point more directly:
# For-loop approach
weights = []
for m in masses:
weights.append(m * 9.8)
# map() approach — same result, fewer lines
weights = list(map(lambda m: m * 9.8, masses))
Filter()
How it works
filter(function, iterable) keeps only the elements for which the function
returns True. The function must return a boolean value (or a value Python
treats as truthy or falsy). The output list may be shorter than the input list.
filter(function, iterable) filter(lambda x: condition, iterable)
Conceptually, for a list [a, b, c, d] and a predicate function
p:
filter(p, [a, b, c, d]) → [x for x in [a,b,c,d] if p(x) is True]
Basic example
numbers = [3, 7, 5, 2, 1, 6] result = list(filter(lambda x: x > 3, numbers)) # Result: [7, 5, 6]
Physics examples
Isolating particles moving above a speed threshold:
speeds = [120, 340, 95, 500, 210, 80] # m/s fast_particles = list(filter(lambda v: v > 200, speeds)) # Result: [340, 500, 210]
Keeping only positive charges from a mixed list:
charges = [-1.6e-19, 1.6e-19, -3.2e-19, 3.2e-19, 0, 1.6e-19] # Coulombs positive = list(filter(lambda q: q > 0, charges)) # Result: [1.6e-19, 3.2e-19, 1.6e-19]
Filtering out particles that have left the simulation boundary:
# Each particle p has a .pos.x attribute representing its x position boundary = 10.0 # meters inside = list(filter(lambda p: abs(p.pos.x) < boundary, particles))
Passing None as the function
A special case: passing None instead of a function removes all
false values from the list (zeros, empty strings, None, False):
messy = [1, 0, 3, None, 5, 0, 7] clean = list(filter(None, messy)) # Result: [1, 3, 5, 7]
Reduce()
How it works
reduce(function, iterable, initializer) cumulatively applies a
two-argument function to the elements of a list, reducing the entire sequence down
to a single scalar value.
The function passed to reduce() must accept exactly two arguments:
- The accumulator — the running result
- The current element — the next item from the list
After each call, the return value becomes the new accumulator for the next call.
from functools import reduce reduce(function, iterable, initializer)
Step-by-step walkthrough
from functools import reduce numbers = [1, 2, 3, 4] result = reduce(lambda x, y: x * y, numbers) # Step 1: x=1, y=2 → 1 * 2 = 2 # Step 2: x=2, y=3 → 2 * 3 = 6 # Step 3: x=6, y=4 → 6 * 4 = 24 # Final result: 24
Physics examples
Adding all masses in a system:
from functools import reduce masses = [1.0, 2.0, 3.0, 4.0] # kg total_mass = reduce(lambda acc, m: acc + m, masses, 0.0) # Result: 10.0 kg
Finding the maximum speed in a list of particles:
from functools import reduce speeds = [3.2, 7.8, 1.1, 9.4, 5.5] # m/s max_speed = reduce(lambda a, b: a if a > b else b, speeds) # Result: 9.4 m/s
Computing total work done by forces over different displacements (W = F·d):
from functools import reduce forces = [10.0, 25.0, 5.0, 40.0] # Newtons displacements = [2.0, 1.5, 3.0, 0.5] # meters work_list = list(map(lambda fd: fd[0] * fd[1], zip(forces, displacements))) total_work = reduce(lambda acc, w: acc + w, work_list, 0.0) # Result: 20.0 + 37.5 + 15.0 + 20.0 = 92.5 Joules
Important warning
Always provide an initializer (third argument) when using reduce().
Calling it on an empty list without an initializer raises a TypeError.
With an initializer, an empty list returns the initializer value:
reduce(lambda acc, x: acc + x, [], 0.0) # Returns 0.0 safely instead of raising an error
Combining map(), filter(), and reduce()
The power of these functions come from chaining them together into a data pipeline. Each function's output becomes the next function's input, creating a clean sequence of transformation → selection → aggregation.
Example — total kinetic energy of only the fast-moving particles in a system:
from functools import reduce
# Step 1: filter() — only keep particles with a speed greater than 3 m/s
moving = list(filter(lambda p: p.speed > 3.0, particles))
# Step 2: map() — calculate the kinetic energy for each particle
ke_list = list(map(lambda p: 0.5 * p.mass * p.speed**2, moving))
# Step 3: reduce() — add all the kinetic energies
total_ke = reduce(lambda acc, ke: acc + ke, ke_list, 0.0)
print("Total KE of fast particles:", round(total_ke, 2), "J")
This three-step pattern (filter, map, reduce) is one of the most widely used patterns in programming.
Interactive Simulation
The following GlowScript simulation demonstrates all three functions working together in a physics context: map(), filter(), and reduce() in VPython Physics — Trinket
The simulation creates five spheres arranged in a horizontal row, each assigned a different mass (ranging from 1 to 8 kg) and a different speed (ranging from 1.5 to 6 m/s). The radius of each sphere is proportional to its mass.
The program then demonstrates each function in sequence:
- map() iterates over the list of masses and computes the gravitational weight
of each sphere using Newton's second law (F = mg, with g = 9.8 m/s²). The results are printed to the console, showing the weight in Newtons for each sphere.
- filter() checks every sphere's speed and keeps only those moving faster than
3.0 m/s. Those spheres are turned red in the 3D scene, providing a visual indicator of which objects meet the threshold condition. The speeds of the selected spheres are also printed.
- reduce() — implemented manually as a custom function since GlowScript does
not support the functools module. It adds the kinetic energy of every sphere into a single total. The individual kinetic energies
and the system total are printed to the console.
Together, the simulation illustrates the filter → map → reduce pipeline in a physical setting, showing how the three functions complement each other.
References
1. Python map, filter, reduce — bogotobogo.com
3. VPython Documentation — vpython.org
4. Python 3 Built-in Functions (map, filter) — Python Software Foundation
5. functools module (reduce) — Python Software Foundation
6. Functional Programming HOWTO — Python Software Foundation
7. Python's map() — Real Python
8. Python's filter() — Real Python
9. Python's reduce() — Real Python
10. Lambda Expressions in Python — Real Python
11. Functional Programming in Python — GeeksforGeeks
12. Higher-Order Functions in Python — GeeksforGeeks