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.