rguides

c()

c(..., recursive = FALSE, USE.NAMES = TRUE)

The c() function (short for “combine” or “concatenate”) is the most fundamental function in R for creating vectors. Almost every R script uses c() to group values together into a single vector. Understanding how c() works is essential for effective R programming.

Parameters

ParameterTypeDefaultDescription
...objectsrequiredValues to combine. Can be vectors, lists, or individual values.
recursivelogicalFALSEIf TRUE, recursively flattens lists into vectors.
USE.NAMESlogicalTRUEIf TRUE and the arguments are named, result inherits those names.

Return value

When recursive = FALSE (default): returns a vector of the same type as the input (or the highest type in the coercion hierarchy: NULL < raw < logical < integer < double < complex < character)

When recursive = TRUE: returns a flattened vector, unlisting all nested structures

Type coercion

R automatically coerces elements to the most flexible type:

# Numeric and character: all become character
c(1, 2, "three")
# [1] "1" "2" "three"

# Integer and numeric: all become numeric
c(1L, 2.5)
# [1] 1.0 2.5

# Mixed logical and numeric: logical TRUE/FALSE becomes 1/0
c(TRUE, 0, 1)
# [1] 1 0 1

Examples

Creating basic vectors

The most common use of c() is creating simple vectors from scratch. Each element you pass becomes a position in the resulting vector, and R infers the type from the values you supply. Numeric vectors store doubles by default, though you can force integers with the L suffix. Character vectors hold strings, and logical vectors store TRUE/FALSE triples. Creating vectors this way is the starting point for nearly every R script.

# Numeric vector
nums <- c(1, 2, 3, 4, 5)
nums
# [1] 1 2 3 4 5

# Character vector
words <- c("apple", "banana", "cherry")
words
# [1] "apple"  "banana" "cherry"

# Logical vector
flags <- c(TRUE, FALSE, TRUE, TRUE)
flags
# [1]  TRUE FALSE  TRUE  TRUE

Combining vectors

Beyond creating fresh vectors, c() excels at concatenating existing ones. You can chain multiple vectors together in a single call, append a value to the end, or prepend one to the beginning. This is how you grow data incrementally during interactive work, though for loops you should pre-allocate rather than repeatedly calling c().

# Concatenate two vectors
a <- c(1, 2, 3)
b <- c(4, 5, 6)
c(a, b)
# [1] 1 2 3 4 5 6

# Append single value to vector
c(a, 7)
# [1] 1 2 3 4 5 6 7

# Prepend value
c(0, a)
# [1] 0 1 2 3

Using recursive = TRUE

The recursive argument changes c() from a vector combiner into a list flattener. When you have a named list whose elements are numeric vectors, setting recursive = TRUE extracts every element and joins them into a flat vector with compound names. This is useful when you need to collapse a nested data structure into a single dimension for downstream computations.

# List with nested vectors
my_list <- list(a = c(1, 2), b = c(3, 4))

# Default (recursive = FALSE) returns a list
c(my_list, recursive = FALSE)
# $a
# [1] 1 2
# $b
# [1] 3 4

# recursive = TRUE flattens to vector
c(my_list, recursive = TRUE)
# a1 a2 b1 b2 
#  1  2  3  4

Named vectors

Naming elements at creation time makes vectors self-documenting. The USE.NAMES parameter controls whether these names survive the combine operation. Setting it to FALSE strips all names, which can be useful when names would be misleading after coercion or when you want to discard labels from upstream data before further processing.

# Automatic naming with USE.NAMES = TRUE (default)
c(a = 1, b = 2, c = 3)
# a b c 
# 1 2 3 

# Disable naming
c(a = 1, b = 2, c = 3, USE.NAMES = FALSE)
# [1] 1 2 3

Common use cases

Building data frame columns

One of the most common applications is constructing data frame columns with c(). Each column must have the same length, and c() is the natural way to supply the values inline. Data frames built this way are immediately ready for analysis with base R or tidyverse functions, making this pattern ubiquitous in data preparation scripts.

df <- data.frame(
  name = c("Alice", "Bob", "Charlie"),
  age = c(25, 30, 35),
  score = c(85.5, 92.0, 78.5)
)

Vector initialization

You can also use c() for initialization patterns. An empty vector starts with c() and grows as results accumulate — fine for interactive exploration. Adding rep() inside c() gives you a pre-filled vector of any desired length, though for tight loops, pre-allocating with vector() and assigning by index is faster than growing with c().

# Create empty vector
empty <- c()

# Pre-allocate with NA
initialized <- c(rep(NA, 10))

Combining different types (Caution!)

One of the sharpest edges in R is c()’s silent type coercion. When you mix types, R promotes everything to the most flexible common denominator without warning. This is especially dangerous when numeric data accidentally picks up a character value, converting an entire column to strings. Always check the result type with typeof() when combining values from different sources.

# This coerces everything to character
mixed <- c(1, "two", 3)
# [1] "1"  "two" "3"

# Check the type
typeof(mixed)
# [1] "character"

c() in practice

c() coerces all arguments to a common type. The coercion hierarchy is: logical < integer < double < complex < character. Mixing a logical with a double produces a double: c(TRUE, 1.5) gives c(1.0, 1.5). Mixing anything with a character produces a character: c(1, "a") gives c("1", "a"). This automatic coercion is the most common source of type bugs in R, inspect the result with typeof() or str() when combining values of different types.

c() flattens its arguments: c(c(1,2), c(3,4)) gives c(1,2,3,4), not a nested vector. Lists are not flattened unless you use c() on lists, in which case each top-level list element becomes a top-level element in the result. To nest vectors without flattening, use list() instead of c().

When c() is called with no arguments, it returns NULL. This makes NULL the identity element for c(): c(NULL, x) and c(x, NULL) both equal x. This property is useful for building vectors incrementally: initialize with result <- NULL, then result <- c(result, new_value) in a loop. For performance, pre-allocate with vector() if the final length is known in advance, since growing a vector with c() in a loop copies the entire vector on each iteration.

Named elements carry their names through c(): c(a = 1, b = 2, 3) returns c(a=1, b=2, 3) where the third element has an empty name. Names from sub-vectors are also preserved.

c() combines values into a vector, coercing all elements to the most flexible type in the hierarchy: logical < integer < double < complex < character. Passing a NULL element has no effect — c(1, NULL, 2) returns c(1, 2). Use list() when you need to preserve mixed types without coercion.

See also