rguides

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

ParameterTypeDefaultDescription
Xvector or listAn atomic vector or list to iterate over
FUNfunctionThe function to apply to each element
FUN.VALUEtemplateA prototype for the expected output: type and length must match exactly
...anyAdditional arguments passed to FUN on each call
USE.NAMESlogicalTRUEIf 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
FunctionOutputSimplificationSafety
lapply()Always listNoneSafe
sapply()Vector, matrix, or listAutomaticUnsafe (silent coercion)
vapply()Vector, matrix, or arrayNone (you declare it)Safe (enforced)
apply()Vector, matrix, or arrayNoneSafe

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.

See Also