purrr::walk()
walk(.x, .f, ...) NULL (invisible .x) · Updated March 13, 2026 · Tidyverse The walk() and walk2() functions from the purrr package apply a function to each element of a list or vector for its side effects—they return the input .x invisibly instead of collecting results. This is useful when you want to perform an action (printing, saving, plotting) on each element without constructing a new list. walk() iterates over a single vector, while walk2() iterates over two vectors in parallel, matching elements by position. Together they provide a clean, pipe‑friendly way to execute loops that produce side effects, keeping your code readable and functional.
Syntax
walk(.x, .f, ...)
walk2(.x, .y, .f, ...)
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
.x | A vector or list | (required) | First vector to iterate over. |
.y | A vector or list | (required) | Second vector to iterate over (for walk2 only). |
.f | A function, formula, or vector | (required) | Function to apply to each element (or pair). If a formula, e.g. ~ .x + 1, it is converted to a function with .x (and .y for walk2) as arguments. |
... | Additional arguments | None | Extra arguments passed on to .f. |
Examples
Basic walk() for printing
library(purrr)
# Create a list of data frames
df_list <- list(
mtcars[1:3, ],
iris[1:3, ],
airquality[1:3, ]
)
# Print each data frame (side effect)
walk(df_list, print)
# mpg cyl disp hp drat wt qsec vs am gear carb
# Mazda RX4 21.0 6 160 110 3.90 2.620 16.46 0 1 4 4
# Mazda RX4 Wag 21.0 6 160 110 3.90 2.875 17.02 0 1 4 4
# Datsun 710 22.8 4 108 93 3.85 2.320 18.61 1 1 4 1
#
# Sepal.Length Sepal.Width Petal.Length Petal.Width Species
# 1 5.1 3.5 1.4 0.2 setosa
# 2 4.9 3.0 1.4 0.2 setosa
# 3 4.7 3.2 1.3 0.2 setosa
#
# Ozone Solar.R Wind Temp Month Day
# 1 41 190 7.4 67 5 1
# 2 36 118 8.0 72 5 2
# 3 12 149 12.6 74 5 3
# The walk() call returns the original df_list invisibly,
# allowing further piping.
result <- walk(df_list, print)
class(result) # "list"
length(result) # 3
walk() with anonymous function for side effects
# Create a directory of CSV files
temp_dir <- tempdir()
walk(1:3, ~ write.csv(mtcars[1:3, ], file.path(temp_dir, paste0("mtcars_", .x, ".csv"))))
# List the created files
list.files(temp_dir, pattern = "mtcars_.*\\.csv")
# [1] "mtcars_1.csv" "mtcars_2.csv" "mtcars_3.csv"
walk2() for parallel iteration
# Two vectors: file names and data frames
file_names <- c("cars.csv", "iris.csv", "air.csv")
data_list <- list(mtcars, iris, airquality)
# Save each data frame with its corresponding name
walk2(data_list, file_names, ~ write.csv(.x, .y))
# Check that files exist
file.exists(file_names)
# [1] TRUE TRUE TRUE
walk2() with formula syntax
# Create a series of simple scatter plots
x_vals <- list(1:10, 11:20, 21:30)
y_vals <- list(rnorm(10), rnorm(10), rnorm(10))
walk2(x_vals, y_vals, ~ plot(.x, .y, main = paste("Plot for", .y[1])))
# Three plot windows appear (side effect).
Common Patterns
Iterating with side effects in a pipeline
Because walk() returns its input invisibly, you can insert side‑effect steps into a pipeline without breaking the flow:
library(dplyr)
library(purrr)
mtcars %>%
split(.$cyl) %>%
walk(~ print(summary(.$mpg))) %>%
map(~ lm(mpg ~ wt, data = .)) %>%
walk2(c("cyl4", "cyl6", "cyl8"), ~ {
png(paste0(.y, ".png"))
plot(.x)
dev.off()
})
Validating data without storing results
Use walk() to run validation checks, throwing an error if any check fails:
validate_data <- function(df) {
walk(names(df), ~ if (any(is.na(df[[tidyverse::]]))) stop("Column ", .x, " contains NA"))
walk(names(df), ~ if (!is.numeric(df[[tidyverse::]])) warning("Column ", .x, " is not numeric"))
invisible(df)
}
validate_data(mtcars) # passes silently
# validate_data(iris) # would warn about Species column
Batch file operations
Combine walk() with fs::dir_create() and walk2() with readr::write_csv() to create and populate directories:
library(fs)
library(readr)
# Create a directory for each species
species <- unique(iris$Species)
walk(species, ~ dir_create(.x))
# Write a CSV for each species into its directory
iris_split <- split(iris, iris$Species)
walk2(iris_split, species, ~ write_csv(.x, path(.y, "data.csv")))