How to Use map_dbl(), map_chr(), map_int(), and map_lgl() in R
Introduction
While map() always returns a list, the typed variants return atomic vectors of a specific type. This is cleaner when you know your function will return a single value of a known type for each element.
The typed map functions: - map_dbl() - returns a numeric (double) vector - map_chr() - returns a character vector - map_int() - returns an integer vector - map_lgl() - returns a logical vector
Getting Started
library(tidyverse)
library(palmerpenguins)map_dbl(): Return Numeric Values
Use map_dbl() when your function returns a single number:
# Create a list of numeric vectors
numbers <- list(
a = c(1, 2, 3, 4, 5),
b = c(10, 20, 30),
c = c(100, 200, 300, 400)
)
# Calculate mean of each - returns a named numeric vector
map_dbl(numbers, mean)Practical example with data
# Get mean of each numeric column in penguins
penguins |>
select(where(is.numeric), -year) |>
map_dbl(\(x) mean(x, na.rm = TRUE))Extract model statistics
# Fit models by species and extract R-squared
penguins |>
drop_na() |>
split(~species) |>
map(\(df) lm(body_mass_g ~ flipper_length_mm, data = df)) |>
map_dbl(\(m) summary(m)$r.squared)map_chr(): Return Character Values
Use map_chr() when your function returns a single string:
# Get class of each column
penguins |>
map_chr(class)Extract names or labels
# Create named list
models <- list(
simple = lm(mpg ~ wt, data = mtcars),
multiple = lm(mpg ~ wt + hp, data = mtcars)
)
# Get formula as string
map_chr(models, \(m) deparse(formula(m)))Format values as strings
# Format numbers with units
measurements <- list(height = 180, weight = 75, age = 30)
units <- c(height = "cm", weight = "kg", age = "years")
map_chr(names(measurements), \(name) {
paste(measurements[[name]], units[name])
})map_int(): Return Integer Values
Use map_int() when your function returns a single integer:
# Count elements in each list item
numbers <- list(
a = 1:5,
b = 1:10,
c = 1:3
)
map_int(numbers, length)Count observations by group
# Count rows in each nested data frame
penguins |>
drop_na() |>
split(~species) |>
map_int(nrow)Count unique values
# Count unique values in each column
penguins |>
map_int(\(x) n_distinct(x, na.rm = TRUE))map_lgl(): Return Logical Values
Use map_lgl() when your function returns TRUE or FALSE:
# Check which columns have missing values
penguins |>
map_lgl(\(x) any(is.na(x)))Filter columns programmatically
# Find numeric columns
penguins |>
map_lgl(is.numeric)
# Use with select
penguins |>
select(where(\(x) map_lgl(list(x), is.numeric)[[1]]))Check conditions across list elements
# Check if all values are positive
numbers <- list(a = c(1, 2, 3), b = c(-1, 2, 3), c = c(5, 6, 7))
map_lgl(numbers, \(x) all(x > 0))Choosing the Right Variant
Use this decision tree:
Does your function return a single value per element?
│
├── No → use map() (returns list)
│
└── Yes → What type?
├── Number (double) → map_dbl()
├── Integer → map_int()
├── Text → map_chr()
├── TRUE/FALSE → map_lgl()
└── Data frame → map_dfr() or list_rbind()
list_rbind() and list_cbind() (purrr 1.0+)
Modern purrr (1.0+) recommends list_rbind() over map_dfr():
# Old way (still works)
map_dfr(1:3, \(i) tibble(x = i, y = i^2))
# New way (purrr 1.0+) - more explicit
map(1:3, \(i) tibble(x = i, y = i^2)) |> list_rbind()
# With ID column
map(1:3, \(i) tibble(x = i, y = i^2)) |>
list_rbind(names_to = "id")The new approach is clearer about what’s happening: first map, then combine.
# Column binding
map(1:3, \(i) tibble(!!paste0("col", i) := rnorm(5))) |>
list_cbind()Combining map_* with Data Frames
Row-binding results with map_dfr()
When each iteration returns a data frame, use map_dfr() to combine them:
# Get summary stats for each species
penguins |>
drop_na() |>
split(~species) |>
map_dfr(\(df) {
tibble(
mean_mass = mean(df$body_mass_g),
sd_mass = sd(df$body_mass_g),
n = nrow(df)
)
}, .id = "species")Column-binding with map_dfc()
# Less common, but binds results as columns
map_dfc(1:3, \(i) tibble(!!paste0("col", i) := rnorm(5)))Common Mistakes
1. Type mismatch errors
# This will error - can't return character from map_dbl
# map_dbl(penguins, class)
# Use map_chr instead
map_chr(penguins, class)2. Functions returning multiple values
Typed map functions expect exactly one value per element:
# This errors - range() returns 2 values
# map_dbl(numbers, range)
# Solution: return a single value or use map()
map_dbl(numbers, \(x) diff(range(x))) # Returns range width
map(numbers, range) # Returns list of ranges3. NULL values causing errors
# If a function can return NULL, it won't work with typed maps
data_with_nulls <- list(a = 1:3, b = NULL, c = 4:6)
# This errors
# map_int(data_with_nulls, length)
# Handle NULLs first
map_int(data_with_nulls, \(x) if(is.null(x)) 0L else length(x))Summary
| Function | Returns | Use When |
|---|---|---|
map() |
list | Multiple values or unknown type |
map_dbl() |
numeric vector | Single number per element |
map_chr() |
character vector | Single string per element |
map_int() |
integer vector | Single integer per element |
map_lgl() |
logical vector | TRUE/FALSE per element |
map_dfr() |
data frame | Data frame per element (row-bind) |
- Typed variants are cleaner than
map() |> unlist() - Ensure your function returns exactly one value of the correct type
- Handle NULL and NA values explicitly