Functional Programming with purrr
purrr is the tidyverse's functional-programming toolkit: it lets you apply a function to every element of a list or vector with map() , replacing hand-written loops with short, type-safe, composable pipelines.
Learn Functional Programming with purrr in our free R course — a beginner-friendly interactive lesson with worked examples, a practice exercise and a quick…
Part of the free R course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.
In this lesson you'll learn map() and its typed variants, the ~ .x formula shorthand, parallel iteration with map2() / pmap() , folding with reduce() , and filtering with keep() / discard() — and how they compare to the base apply family.
What You'll Learn in This Lesson
1️⃣ map() and the Typed Variants
The core verb is map() : give it a list or vector and a function, and it runs the function on each element, returning a list of the same length. You can pass a function by name, write one inline, or use the formula shorthand.
A plain map() always returns a list, which is awkward if you wanted a numeric vector. The typed variants return an atomic vector of exactly that type — and error loudly if a result doesn't fit, catching bugs early.
2️⃣ The ~ .x Shorthand & Parallel Maps
For short transformations, the ~ formula is the idiomatic purrr style: .x stands for the current element. It's just a compact way to write an anonymous function.
When you need to walk two or more vectors in lockstep, reach for map2() (exactly two, with .x and .y ) or pmap() (any number, passed as a list).
3️⃣ reduce(), keep() & discard()
Not every job is one-to-one. reduce() folds a whole list into a single value, and keep() / discard() filter a list by a predicate. Together with map() , they let you express most iteration as one readable pipe.
Notice the final pipe: keep then map_dbl then reduce . Each verb does one thing, and the data flows left to right — far easier to read and modify than a nested for loop.
Your turn. Fill in the # TODO blank and run it.
This is the everyday purrr task: summarising a list of vectors without a single loop. Combine map_dbl() , map_int() , keep() , and reduce() .
📋 Quick Reference — purrr
Practice quiz
What does a bare map() always return?
- A numeric vector
- A list, one element per input
- A data frame
- A single value
Answer: A list, one element per input. map() always returns a list of the same length as its input.
Which variant guarantees a double (numeric) vector or errors?
- map_dbl()
- map()
- map_chr()
- map_lgl()
Answer: map_dbl(). map_dbl() returns a double vector and errors if a result is not a single double.
In a ~ formula, what does .x refer to?
- The output list
- The function name
- The current element being processed
- The index position
Answer: The current element being processed. .x is the current element inside purrr's formula shorthand.
Which function walks TWO vectors in parallel?
- pmap()
- map()
- reduce()
- map2()
Answer: map2(). map2() iterates over exactly two vectors, exposing .x and .y.
What does reduce(1:5, ) return?
- 15
- A list of 5 sums
- 120
- 5
Answer: 15. reduce() folds with + : 1+2+3+4+5 = 15.
Which helper keeps only elements matching a predicate?
- discard()
- keep()
- map_lgl()
- filter()
Answer: keep(). keep() retains elements for which the predicate is TRUE; discard() removes them.
Which typed variant returns a character vector?
- map_int()
- map_dbl()
- map_chr()
- map_lgl()
Answer: map_chr(). map_chr() returns a character vector, erroring on a type mismatch.
How do map() and reduce() differ?
- map() folds to one value; reduce() is one-to-one
- map() is one-to-one; reduce() folds many into one value
- They are identical
- reduce() only works on numbers
Answer: map() is one-to-one; reduce() folds many into one value. map() transforms each element independently; reduce() collapses the list to a single value.
Why are typed map_*() variants safer than base sapply()?
- They run faster
- They print nicer output
- They promise an exact type or error, never silently guess
- They avoid loading packages
Answer: They promise an exact type or error, never silently guess. sapply() guesses its return type; the typed map variants guarantee one or fail loudly.
How do you access the value of a reactive-style purrr formula's second input in map2()?
- .z
- .x only
- .y
- ..2 only
Answer: .y. In map2(), .x is the first vector's element and .y the second's.