vapply()
vapply(X, FUN, FUN.VALUE, ..., USE.NAMES = TRUE) vapply() is the type-safe cousin of sapply(). It applies a function to each element of a vector or list, but unlike sapply() which silently coerces output, vapply() requires you to declare the expected return shape via the FUN.VALUE argument. If any call to FUN produces a value that does not match that declared shape, R stops immediately with an error.
This makes vapply() the go-to choice for production code where silent type coercion would cause subtle bugs.
Syntax
vapply(X, FUN, FUN.VALUE, ..., USE.NAMES = TRUE)
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
X | vector or list | — | An atomic vector or list to iterate over |
FUN | function | — | The function to apply to each element |
FUN.VALUE | template | — | A prototype for the expected output: type and length must match exactly |
... | any | — | Additional arguments passed to FUN on each call |
USE.NAMES | logical | TRUE | If TRUE and X has names, those names are used for the result |
Examples
Basic usage with character vectors
cities <- c("New York", "London", "Tokyo", "Paris")
result <- vapply(cities, nchar, FUN.VALUE = integer(1))
result
# [1] 8 6 5 5
nchar() returns a single integer per string. FUN.VALUE = integer(1) declares this contract explicitly. The result is a plain integer vector.
Returning a named vector per element
first_and_last <- function(x) {
chars <- strsplit(x, "")[[1]]
c(first = chars[1], last = chars[length(chars)])
}
letters <- c("apple", "banana", "cherry")
result <- vapply(letters, first_and_last, FUN.VALUE = character(2))
result
# [,1] [,2] [,3]
# first "a" "b" "c"
# last "e" "a" "y"
FUN.VALUE = character(2) tells R the function returns a character vector of length 2. Since FUN.VALUE has length greater than 1, the result is a matrix with rows matching the FUN.VALUE length.
Passing extra arguments
compute_range <- function(x, mult = 1) {
r <- range(x)
c(min = r[1] * mult, max = r[2] * mult)
}
numbers <- list(c(1, 5, 3), c(10, 2, 8), c(6, 6, 6))
result <- vapply(numbers, compute_range, FUN.VALUE = numeric(2), mult = 2)
result
# [,1] [,2] [,3]
# min 2 10 12
# max 10 16 12
Extra arguments go after FUN.VALUE in the ... slot.
Type mismatch causes an error
# This throws an error: nchar returns integer, not character
result <- vapply(cities, nchar, FUN.VALUE = character(1))
# Error in vapply(): values must be character, not integer
This strictness is the feature, not a bug. It catches type errors early.
Common Patterns
Ensuring consistent output from a custom function
get_stats <- function(x) {
c(mean = mean(x), sd = sd(x))
}
datasets <- list(
control = rnorm(50, mean = 0, sd = 1),
treatment = rnorm(50, mean = 2, sd = 1)
)
vapply(datasets, get_stats, FUN.VALUE = numeric(2))
# [,1] [,2]
# mean 0.1373522 2.0939492
# sd 0.9408999 0.9165887
Safe extraction from a list of data frames
extract_nrow <- function(df) nrow(df)
data_frames <- list(a = data.frame(x = 1:5), b = data.frame(y = 1:3))
vapply(data_frames, extract_nrow, FUN.VALUE = integer(1))
# a b
# 5 3
How vapply Compares to Related Functions
| Function | Output | Simplification | Safety |
|---|---|---|---|
lapply() | Always list | None | Safe |
sapply() | Vector, matrix, or list | Automatic | Unsafe (silent coercion) |
vapply() | Vector, matrix, or array | None (you declare it) | Safe (enforced) |
apply() | Vector, matrix, or array | None | Safe |
The key distinction from sapply() is that vapply() never attempts simplification. The output shape is entirely determined by FUN.VALUE. If FUN.VALUE is a scalar (e.g., integer(1)), you get a vector. If FUN.VALUE has length 2, you get a 2-row matrix.