lapply() in R: Apply a Function to a List or Vector
The lapply() function in base R applies a function to every element of a list or vector and returns a LIST of the results. Unlike sapply(), it always returns a list regardless of result shapes.
lapply(1:5, function(x) x^2) # list of 5 numbers lapply(1:5, sqrt) # list of 5 sqrt values lapply(list(a=1:3, b=4:6), mean) # list with names lapply(mtcars, mean) # one mean per column result <- lapply(files, read.csv) # read many files into list do.call(rbind, lapply(...)) # combine list of df into one df purrr::map(1:5, ~ .^2) # tidyverse equivalent
Need explanation? Read on for examples and pitfalls.
What lapply() does in one sentence
lapply(X, FUN) applies FUN to each element of X and returns a list of length equal to X, with each element being the result of one call. Output structure is always a list, regardless of what FUN returns.
lapply is the workhorse of "apply this function to every X" patterns. The list output is consistent and predictable, making it safer for production code than sapply().
Syntax
lapply(X, FUN, ...). X is a list or vector; FUN is the function; ... passes extra arguments to FUN.
Each call returns one number; the result is a list of 5 single-number elements.
unlist() or do.call(c, ...). unlist(lapply(1:5, sqrt)) returns a flat numeric vector. sapply() would have done this automatically, but lapply + unlist makes the conversion explicit.Five common patterns
1. Apply to a vector
2. Apply to a named list
Names are preserved: result is a named list.
3. Apply over data frame columns
Data frames ARE lists of columns. lapply iterates column-wise. To get a vector instead of list, use sapply or unlist().
4. Read multiple files
Common pattern: read many files, get a list of data frames. Combine with do.call(rbind, dfs) or dplyr::bind_rows(dfs) for a single combined data frame.
5. Pass extra arguments through ...
mean is called with (element, na.rm = TRUE) for each list element. Extra args after FUN go through.
lapply() is the safest of the apply family because it ALWAYS returns a list, regardless of what the function returns. sapply() may unexpectedly return a matrix if all calls produce same-length vectors. vapply() errors on type mismatch. lapply quietly handles any shape, useful when you don't know what FUN will return.lapply vs sapply vs purrr::map
| Function | Output | When to use |
|---|---|---|
lapply() |
List | When you want a list (predictable) |
sapply() |
Vector / matrix / list (auto) | Interactive, simple cases |
vapply() |
Vector of declared type | Production, type-safe |
purrr::map() |
List (tidyverse) | Tidyverse pipelines |
purrr::map_dbl() |
Numeric vector | Type-safe tidyverse |
When to use which:
- Use
lapply()when you want predictable list output. - Use
sapply()for quick interactive simplification. - Use
vapply()orpurrr::map_*()for type-safe production code.
Common pitfalls
Pitfall 1: confusing lapply with for loops. lapply is a FUNCTION; it returns a value. for loops are statements that update side effects. To collect results from a loop, use lapply or pre-allocate a list and assign by index.
Pitfall 2: forgetting to unlist for vector ops. mean(lapply(x, fn)) errors because mean does not work on a list. Use unlist() or sapply(): mean(unlist(lapply(x, fn))).
lapply does NOT modify in place. lapply(my_list, fn) returns a NEW list; the original my_list is unchanged. To update the original, assign: my_list <- lapply(my_list, fn).Try it yourself
Try it: Use lapply to compute the standard deviation of each numeric column in mtcars. Save to ex_sds.
Click to reveal solution
Explanation: lapply(mtcars, sd) applies sd() to each column. Result is a named list. To get a numeric vector instead, use sapply or unlist(lapply(...)).
Related apply functions
After mastering lapply, look at:
sapply(): simplifies output to vector/matrix when possiblevapply(): type-strict version for safetymapply(): multi-argument applyapply(): for matrix rows/columns specificallypurrr::map()and family: tidyverse alternativesMap(): multi-list apply (functional programming style)
For data frame column transforms, dplyr::mutate(across(...)) is more idiomatic than lapply.
FAQ
What is the difference between lapply and sapply in R?
lapply() always returns a LIST. sapply() tries to simplify the result to a vector or matrix; falls back to a list if shapes vary. Use lapply for predictable list output; sapply for convenient interactive use.
How do I apply a function to each element in R?
lapply(x, fn) for list output. sapply(x, fn) for simplified output. purrr::map(x, fn) for tidyverse style. All apply fn to each element of x.
How do I pass extra arguments to FUN in lapply?
After FUN, list extra arguments: lapply(x, fn, arg1 = val1, arg2 = val2). They are passed to fn for every call.
How do I combine the results of lapply into a single data frame?
Use do.call(rbind, lapply(...)) or dplyr::bind_rows(lapply(...)). Both stack list-of-data-frames into one data frame vertically.
Should I use lapply or for loop?
For collecting results, lapply is cleaner and idiomatic. For side effects (printing, writing files), a for loop is fine. Performance is similar in modern R; readability is the main concern.