purrr::map2

Updated April 20, 2026 · Tidyverse
purrr tidyverse iteration functional programming parallel iteration

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

ParameterDescription
.xFirst vector or list to iterate over.
.ySecond vector or list to iterate over. Must be the same length as .x.
.fA 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