purrr map_vec() in R: Map to a Simple Typed Vector
purrr map_vec() applies a function to every element of a list or vector and returns a simplified vector, automatically picking the common output type including Date, factor and other S3 classes.
map_vec(1:3, ~ .x * 2) # numeric vector map_vec(x, toupper) # character vector map_vec(dates, as.Date) # Date vector, class kept map_vec(codes, ~ factor(.x)) # factor vector map_vec(x, f, .ptype = integer()) # force the output type map_vec(x, f, .progress = TRUE) # show a progress bar
Need explanation? Read on for examples and pitfalls.
What map_vec() does in one sentence
map_vec() is the type-flexible simplifier of the purrr map family. You hand it a list or vector and a function, it runs that function once per element, then combines the length-one results into a single atomic or S3 vector. Unlike map_chr() or map_dbl(), it does not force a type in advance. It inspects what the function returns and picks the common type automatically.
That flexibility is the reason map_vec() exists. The typed variants only know about logical, integer, double and character. map_vec() also handles Date, factor, POSIXct and difftime, so iterations that produce those classes finally have a simplifying map function.
Syntax and arguments
The signature takes the data first, then the function. Like every map variant, map_vec() follows the standard purrr argument order, with two extras for type control.
The .f argument accepts a named function such as toupper, a formula like ~ .x * 2 where .x is the current element, or a string or integer that plucks an element from nested data. The .ptype argument is the safety valve: pass it a zero-length prototype like integer() and map_vec() coerces every result to that type or errors trying.
Worked examples
Example 1 squares each number and returns a numeric vector. With a simple numeric function, map_vec() behaves like map_dbl().
Example 2 parses date strings and keeps the Date class. This is the case the typed variants cannot handle, since there is no map_date().
A plain map_chr() here would strip the class and leave you with bare strings. map_vec() returns a genuine Date vector you can subtract, sort, or plot.
Example 3 builds a factor from integer codes. Each call returns a length-one factor, and map_vec() binds them while preserving the levels.
Example 4 forces the output type with .ptype. Supplying a prototype makes the contract explicit and rejects any result that cannot be coerced.
.ptype when you know the type you expect. Passing .ptype = double() or .ptype = Sys.Date() turns map_vec() into a typed variant on demand. It documents intent and fails fast if a function ever returns the wrong class.map_vec() vs map_chr(), map_dbl() and map()
The difference is who decides the output type. All four iterate identically; they differ in what they return and how strict they are.
| Function | Returns | Use when |
|---|---|---|
map() |
A list | Results vary in type or length |
map_chr() |
Character vector | Every result is a single string |
map_dbl() |
Double vector | Every result is a single number |
map_vec() |
Simplest common vector | Results are scalars, including Date or factor |
list_rbind() |
Data frame | Each result is a row |
The contrast with map() is clearest on the same input:
Decision rule: if every result is a scalar and you want a vector out, use map_vec(). If you specifically need numbers or strings and want the type checked, the typed variants state that intent more loudly. If results vary in length, stay with map().
map() and then a manual do.call(c, ...) or reduce(). map_vec() makes that one call, and uses vctrs rules so the combination is type-safe rather than a silent coercion.Common pitfalls
Pitfall 1: the function returns more than one value. map_vec() can only simplify length-one results into its output vector.
The fix is to collapse the result, or switch to map() when varying lengths are genuinely expected.
Pitfall 2: the results have no common type. If one call returns a number and another a string, map_vec() refuses to combine them rather than coercing silently.
Pitfall 3: reaching for map_vec() when the type is fixed. If every result is definitely a number, map_dbl() documents that better and the error message is clearer. Save map_vec() for the cases the typed variants cannot express.
packageVersion("purrr") and upgrade if it reports a version below 1.0.0.Try it yourself
Try it: Use map_vec() to convert the character vector c("3", "7", "11") to a numeric vector. Save the result to ex_nums.
Click to reveal solution
Explanation: as.numeric() returns a single number per element, so map_vec() detects the common double type and binds the three results into a numeric vector.
Related purrr functions
These functions pair naturally with map_vec() in everyday pipelines.
map(): the untyped base case that returns a list when results vary.map_chr(): the string-only sibling for results that are single characters.map_dbl(): the numeric-only sibling for results that are single doubles.map2(): iterates over two inputs in parallel, with its own_vecform.list_rbind(): stacks per-element results into a single data frame.
map_vec(x, f) is close to vapply(x, f, FUN.VALUE) but you do not have to spell out the prototype. map_vec() infers the common type, and only needs .ptype when you want to lock it down.FAQ
What is the difference between map_vec() and map()?
map() always returns a list, no matter what the function produces. map_vec() simplifies the results into a single atomic or S3 vector, choosing the common type automatically. Use map_vec() when each result is a scalar and you want a flat vector to work with directly. Use map() when results differ in length or have no shared type, then process the list afterward.
When was map_vec() added to purrr?
map_vec() arrived in purrr 1.0.0, released in December 2022, alongside other modernised iteration helpers. If your code calls map_vec() and R reports that it cannot find the function, your purrr is older than 1.0.0. Check with packageVersion("purrr") and run install.packages("purrr") to upgrade to a current release.
Can map_vec() return a Date or factor vector?
Yes, and that is its main advantage over the typed variants. map_chr(), map_dbl(), map_int() and map_lgl() only know the four base atomic types. map_vec() understands any vector type that vctrs can combine, so functions returning Date, POSIXct, factor or difftime values simplify correctly into a vector of that class.
Why does map_vec() throw a "Result must be length 1" error?
That error means the function returned more than one value for at least one element. map_vec() simplifies by stacking length-one results, so a length-two return has no place to go. The message reports the failing index. Either collapse the result to a single value, or switch to map() if varying lengths are part of your design.
Is map_vec() slower than map_chr() or map_dbl()?
In practice the difference is negligible for typical data. map_vec() does slightly more work because it inspects results to find the common type, while map_dbl() knows the type up front. For tight loops over millions of elements the typed variant has a small edge, but for everyday iteration the gap is not noticeable.