Reduce() in R: Fold a Function Over a List or Vector
The Reduce() function in base R folds a binary function across a list or vector, collapsing many values into a single accumulated result. It powers running totals, list merges, and any "combine pairwise from the left" computation.
Reduce("+", 1:5) # 15 (sum via fold)
Reduce("+", 1:5, accumulate = TRUE) # 1 3 6 10 15 (running totals)
Reduce("+", 1:5, accumulate = TRUE, init = 100) # 100 101 103 106 110 115
Reduce(paste, letters[1:4]) # "a b c d" (left fold)
Reduce(paste, letters[1:4], right = TRUE) # "a b c d" (right fold)
Reduce(intersect, list(1:5, 3:7, 4:10)) # 4 5 (common elements)
Reduce(merge, list(df1, df2, df3)) # merge many data framesNeed explanation? Read on for examples and pitfalls.
What Reduce() does in one sentence
Reduce(f, x) collapses the elements of x into a single value by applying the binary function f left-to-right, like f(f(f(x1, x2), x3), x4). It is R's left fold, the same operation as foldl in Haskell or functools.reduce in Python.
Reduce belongs to base R's functional programming toolkit (alongside Map, Filter, Find, Position, and Negate). It is the only base function that turns a list of values into one accumulated value by repeated pairwise combination.
Syntax
Reduce(f, x, init, accumulate = FALSE, right = FALSE). f is a binary function, x is a list or vector; init seeds the fold, accumulate returns every step, right flips direction.
The call expands to ((((1 + 2) + 3) + 4) + 5). The first argument can be any binary function: a name in quotes, a function object, or an anonymous function.
"+" looks it up as a function. Reduce("+", x) is identical to Reduce(+, x) or Reduce(function(a, b) a + b, x). Use the quoted form for arithmetic and logical operators; it reads cleanest.Six common patterns
1. Fold an operator across a vector
For sums and products specifically, sum() and prod() are faster and clearer. Use Reduce when the operation is not vectorized (merging, intersecting, composing).
2. Get every intermediate result with accumulate
accumulate = TRUE returns a vector of length length(x), one entry per fold step. This is the cumsum() equivalent for arbitrary binary functions.
3. Seed the fold with init
init prepends a starting value. The output now has length length(x) + 1. Without init and with an empty x, Reduce returns NULL, which often crashes downstream code. Pass init whenever x might be empty.
4. Merge a list of data frames
This is the canonical use case. merge only takes two data frames, so any chain longer than two needs a fold. dplyr::full_join works the same way with purrr::reduce.
5. Intersect many sets
intersect() is binary. Reduce(intersect, list_of_vectors) finds elements present in every vector. The same pattern works with union for the opposite operation.
6. Right fold for right-associative ops
For associative operations like +, the final value is identical regardless of direction. For non-associative ones (function composition, subtraction, division, list concatenation), right = TRUE matters.
merge() works on two tables, but Reduce(merge, list_of_tables) works on any number. union() joins two vectors, but Reduce(union, list) joins many. Whenever you need a binary primitive to apply across a collection, reach for Reduce.Reduce vs apply family vs purrr::reduce
Reduce is a fold; apply functions are maps. A fold collapses a sequence into one value by threading a running result through each step; a map produces one output per input independently. The table below shows where Reduce fits among related base R and purrr primitives.
| Function | Output | Purpose |
|---|---|---|
Reduce(f, x) |
One value | Fold (combine pairwise) |
lapply(x, f) |
List of N values | Map (apply independently) |
sapply(x, f) |
Vector of N values | Map with simplification |
purrr::reduce(x, f) |
One value | Tidyverse fold |
purrr::accumulate(x, f) |
Vector of N values | Tidyverse running fold |
do.call(f, list) |
One value | Call variadic fn on a list |
When to use which:
- Use
Reduce()when the operation is pairwise and the function is strictly binary. - Use
lapplyorsapplywhen you want one result per input, not a single collapsed result. - Use
purrr::reduce()in tidyverse pipelines; semantics are the same asReducewith a friendlier interface. - Use
do.call()when the function takes variable arguments natively (e.g.,do.call(rbind, list_of_dfs)).
Common pitfalls
Pitfall 1: empty input with no init returns NULL. Reduce("+", integer(0)) returns NULL, not 0. Always pass init when the input might be empty: Reduce("+", x, init = 0).
Pitfall 2: confusing accumulate output length. Without init, accumulate = TRUE returns length(x) values. With init, it returns length(x) + 1. Off-by-one bugs are common when chaining the result.
Pitfall 3: using Reduce when vectorized math exists. Reduce("+", 1:1e6) is much slower than sum(1:1e6). Reach for Reduce only when no vectorized primitive exists.
f(accumulator, next_element) is the order in a left fold. If your function is asymmetric (e.g., setdiff), the first argument is the running result and the second is the next input. Get this backwards and the output silently changes meaning.Try it yourself
Try it: Use Reduce with accumulate = TRUE and init = 1 to produce the running product of 1:5. Save the result to ex_running_prod.
Click to reveal solution
Explanation: init = 1 seeds the fold at 1 (the multiplicative identity). accumulate = TRUE returns every intermediate product. The output has length 6: the init plus one per input.
Related base R functional functions
After mastering Reduce, look at:
Map(): apply a function across multiple lists in parallelFilter(): keep elements matching a predicateFind()andPosition(): first element (or index) matching a predicateNegate(): invert a predicate functionpurrr::reduce()andpurrr::accumulate(): tidyverse equivalents
For function composition specifically, Reduce(function(f, g) function(x) f(g(x)), list_of_fns) builds a single pipeline function. Most users prefer magrittr::%>% or the native |> pipe for composition; Reduce is the explicit functional form. See the base funprog reference for the full base R functional-programming family.
FAQ
What is Reduce() in R used for?
Reduce() folds a binary function across a list or vector to produce a single accumulated value. Common uses include summing or multiplying a sequence, merging a list of data frames, intersecting multiple sets, and computing running totals with accumulate = TRUE. It is R's left-fold primitive.
What is the difference between Reduce and lapply in R?
lapply(x, f) calls f on each element of x independently and returns a list of N results. Reduce(f, x) calls a binary f pairwise, threading the running result into each next call, and returns ONE final value. Use lapply to map; use Reduce to fold.
How do I use Reduce with accumulate in R?
Pass accumulate = TRUE: Reduce(f, x, accumulate = TRUE) returns every intermediate result, one per step. With an init value, the output length is length(x) + 1. This is the general-purpose cumsum() equivalent for any binary function.
What is the difference between Reduce and purrr::reduce?
Behavior is identical: both fold a binary function across a sequence. purrr::reduce() has a tidyverse-friendly signature (reduce(x, f, .init = ...)) and integrates cleanly with the pipe. Reduce() is base R with no package dependency. Use purrr::reduce inside tidyverse pipelines, Reduce everywhere else.
Can Reduce work on an empty vector?
Yes, but only if you pass init. Reduce(f, character(0), init = "") returns "". Without init, Reduce returns NULL on empty input, which often crashes downstream code. Always pass init whenever the input might be empty.