purrr::map2
Overview
map2() applies a function to pairs of elements from two vectors or lists, running the function once per position. It’s the two-input version of map(). If you need to iterate over more than two inputs simultaneously, see pmap() instead.
Like map(), map2() returns a list by default, with type-specific variants (map2_dbl(), map2_chr(), etc.) that return atomic vectors directly.
Installation
library(purrr)
Signature
map2(.x, .y, .f, ...)
map2_lgl(.x, .y, .f, ...)
map2_int(.x, .y, .f, ...)
map2_dbl(.x, .y, .f, ...)
map2_chr(.x, .y, .f, ...)
map2_df(.x, .y, .f, ...)
Parameters
| Parameter | Description |
|---|---|
.x | First vector or list to iterate over. |
.y | Second vector or list to iterate over. Must be the same length as .x. |
.f | A function, formula, or atom. |
... | Additional arguments passed to .f for each call. |
Return Value
map2() returns a list the same length as .x and .y. Type-specific variants return atomic vectors. Both inputs must have the same length — if they differ, map2() raises an error.
Basic Usage
Pairwise combination
first_names <- c("Harry", "Ron", "Hermione")
last_names <- c("Potter", "Weasley", "Granger")
map2_chr(first_names, last_names, ~ paste(.x, .y))
# [1] "Harry Potter" "Ron Weasley" "Hermione Granger"
Element-wise arithmetic
prices <- c(10.99, 5.49, 8.99)
tax_rate <- c(0.08, 0.10, 0.08)
map2_dbl(prices, tax_rate, ~ .x * .y)
# [1] 0.8792 0.5490 0.7192
Passing additional arguments
base_url <- c("https://api.example.com", "https://api.test.com")
endpoints <- c("/users", "/products")
map2_chr(base_url, endpoints, ~ paste0(.x, .y, "?key=abc"))
# [1] "https://api.example.com/users?key=abc"
# [2] "https://api.test.com/products?key=abc"
Formula and anonymous function shorthand
numbers <- list(c(1, 2, 3), c(4, 5, 6))
multipliers <- list(10, 100)
map2(numbers, multipliers, ~ .x * .y)
# [[1]]
# [1] 10 20 30
# [[2]]
# [1] 400 500 600
Extracting paired elements
x <- list(list(a = 1, b = 2), list(a = 3, b = 4))
y <- list(list(a = 5, b = 6), list(a = 7, b = 8))
map2_dbl(x, y, ~ .x$a + .y$b)
# [1] 6 10
Common Use Cases
Combining columns from two data frames
library(dplyr)
df1 <- tibble(name = c("Alice", "Bob"), age = c(25, 30))
df2 <- tibble(city = c("London", "Berlin"), country = c("UK", "Germany"))
# Combine into a single tibble row-wise
pmap_dfr(list(name = df1$name, city = df2$city, country = df2$country), tibble)
# # A tibble: 2 x 3
# name city country
# 1 Alice London UK
# 2 Bob Berlin Germany
# Or simpler - just use cbind for this case, but map2 helps when you need transformation:
pmap_chr(list(df1$name, df2$city), ~ paste0(.x, " (", .y, ")"))
# [1] "Alice (London)" "Bob (Berlin)"
Parallel element-wise operations
lat <- c(51.5074, 40.7128, 48.8566)
lon <- c(-0.1278, -74.0060, 2.3522)
map2_chr(lat, lon, ~ sprintf("(%.4f, %.4f)", .x, .y))
# [1] "(51.5074, -0.1278)" "(40.7128, -74.0060)" "(48.8566, 2.3522)"
Generating paired labels
x_labels <- c("alpha", "beta", "gamma")
y_labels <- c("low", "medium", "high")
map2_chr(x_labels, y_labels, ~ paste0(.x, "_", .y))
# [1] "alpha_low" "beta_medium" "gamma_high"
map2 vs mapply
map2() is the tidyverse equivalent of base R’s mapply(). The key difference is syntax:
# Base R mapply
mapply(paste, c("a", "b"), c("1", "2"), MoreArgs = list(sep = "-"))
# [1] "a-1" "b-2"
# purrr map2
map2_chr(c("a", "b"), c("1", "2"), ~ paste(.x, .y, sep = "-"))
# [1] "a-1" "b-2"
mapply() has a SIMPLIFY argument that tries to collapse the result to a matrix or vector. map2() variants make the expected output type explicit.
Gotchas
Length mismatch throws an error. Unlike mapply() which recycles inputs of different lengths (with a warning), map2() requires equal-length inputs:
map2(c("a", "b", "c"), c("1", "2"), paste)
# Error: `.x` and `.y` must be the same size.
# `.x` has size 3. `.y` has size 2.
No index argument in map2. If you need the position as well, use imap() for one input or convert to a data frame first:
x <- c("a", "b", "c")
imap_chr(x, ~ paste0(.y, ": ", .x))
# [1] "1: a" "2: b" "3: c"
For two inputs with index, consider using pmap() with a named list or seq_along() for clarity.
map2 always returns list for multiple outputs. If your function returns a different-shaped object per call, map2() preserves the list structure:
x <- list(c(1, 2), c(3, 4, 5))
y <- list(c("a", "b"), c("c", "d", "e"))
map2(x, y, paste)
# [[1]]
# [1] "1 a" "2 b"
# [[2]]
# [1] "3 c" "4 d" "5 e"
See Also
- /reference/tidyverse/purrr-map/ — iterate over a single vector with
map() - /reference/tidyverse/purrr-reduce/ — reduce a list to a single value by iteratively combining elements
- /guides/purrr-functional-programming/ — full guide to functional programming in R with purrr