Reduce(), Filter(), Map() in Base R: Functional Trifecta Explained
Base R has three powerful higher-order functions: Reduce() collapses a list into a single value, Filter() keeps elements matching a condition, and Map() applies a function across parallel inputs. No packages needed.
These three functions come from functional programming tradition and exist in nearly every language (fold, filter, map). In R, they're capitalized — Reduce, Filter, Map — to distinguish them from other base functions.
Reduce(): Collapse a List to One Value
Reduce(f, x) takes a binary function f and a list x, then combines elements pairwise from left to right: f(f(f(x[1], x[2]), x[3]), x[4]).
Reduce with accumulate = TRUE
Set accumulate = TRUE to see every intermediate result, not just the final one.
Reduce with init
Provide an initial value with init. This is useful when the list might be empty or when you need a specific starting value.
Practical: Merging Multiple Data Frames
Practical: Combining Conditions
Filter(): Keep Matching Elements
Filter(f, x) applies a predicate function f to each element of x and keeps only the elements where f returns TRUE.
Filter on Data Frames
When applied to a data frame, Filter() operates on columns (since a data frame is a list of columns).
Find(): Return First Match
Find() is like Filter() but returns only the first matching element.
Position(): Find the Index
Position() returns the index of the first matching element instead of the element itself.
Map(): Parallel Iteration
Map(f, ...) applies function f to corresponding elements of the input lists. It's the multi-input version of lapply().
Map vs mapply
Map() always returns a list. mapply() tries to simplify the result to a vector or matrix.
Comparison: Base R vs purrr
| Task | Base R | purrr |
|---|---|---|
| Map over 1 input | lapply(x, f) |
map(x, f) |
| Map over 2 inputs | Map(f, x, y) |
map2(x, y, f) |
| Map over N inputs | Map(f, ...) |
pmap(list(...), f) |
| Filter elements | Filter(f, x) |
keep(x, f) |
| Remove elements | Filter(Negate(f), x) |
discard(x, f) |
| Reduce/fold | Reduce(f, x) |
reduce(x, f) |
| Find first match | Find(f, x) |
detect(x, f) |
| Find position | Position(f, x) |
detect_index(x, f) |
Use base R functions (
Reduce,Filter,Map) when you want zero dependencies. Use purrr equivalents when you want typed outputs, formula shorthand, and richer error handling.
Practice Exercises
Exercise 1: Reduce a Shopping Cart
Calculate the total price of items with a discount applied sequentially.
Click to reveal solution
```rExercise 2: Filter and Find
From a list of measurements, filter valid ones, find the first outlier, and get its position.
Click to reveal solution
```rSummary
| Function | Purpose | Returns |
|---|---|---|
Reduce(f, x) |
Collapse list pairwise | Single value |
Reduce(f, x, accumulate=TRUE) |
Show all intermediate results | Vector |
Filter(f, x) |
Keep elements where f is TRUE | Filtered list/vector |
Find(f, x) |
First element where f is TRUE | Single element |
Position(f, x) |
Index of first TRUE | Integer |
Map(f, ...) |
Apply f to parallel inputs | List |
FAQ
Why is Reduce capitalized?
Base R capitalizes Reduce, Filter, Map, Find, and Position to avoid conflicts with common variable names. Lowercase filter would clash with dplyr::filter() and stats::filter().
Can Reduce work right-to-left?
Yes. Set right = TRUE for right-to-left reduction: Reduce(f, x, right = TRUE) computes f(x[1], f(x[2], f(x[3], x[4]))).
What happens if I pass an empty list to Reduce?
Without init, an empty list causes an error. With init, it returns init. Always provide init when the input could be empty.
What's Next?
- Functional Programming in R — the bigger FP picture
- purrr map() Variants — purrr's enhanced versions of Map
- Memoization in R — cache function results for speed