rguides

union()

union(x, y)

The union() function returns all unique elements that appear in either of two vectors. It is a convenient way to merge sets of values without keeping duplicates.

Syntax

union(x, y)

Parameters

ParameterTypeDescription
xvectorFirst input vector
yvectorSecond input vector

Internally, union(x, y) is equivalent to unique(c(x, y)) — the two vectors are concatenated and then deduplicated. The result preserves order: all elements from x appear first in their original sequence, followed by elements from y that were not already present in x. This deterministic ordering makes the output predictable even though the operation is fundamentally set-theoretic.

Examples

Basic usage

x <- c(1, 2, 3)
y <- c(3, 4, 5)

union(x, y)
# [1] 1 2 3 4 5

The same set logic applies to character data. When you have two vectors of strings — such as two lists of names, tags, or identifiers that may partially overlap — union() combines them into a single vector that contains each distinct value once. This is handy when merging categorical levels, tag sets, or unique identifiers collected from separate sources.

Character vectors

a <- c("apple", "banana")
b <- c("banana", "cherry")

union(a, b)
# [1] "apple"  "banana" "cherry"

Duplicates within each input vector are irrelevant to the result — union() eliminates them regardless of whether they occur in the same input or across both inputs. Internally the function calls unique() on the concatenated vector c(x, y), so any value that appears multiple times in x is first reduced to a single occurrence, and the same happens for y, before the two de-duplicated sets are merged.

Handling duplicates

x <- c(1, 1, 2, 2)
y <- c(2, 2, 3, 3)

union(x, y)
# [1] 1 2 3

Common patterns

When you need the union of more than two vectors — for example, combining identifiers from three or more data sources — you can chain union() calls or use Reduce(). Chaining works for a small fixed number of inputs, but Reduce(union, list_of_vectors) scales to any number of vectors and keeps the code readable. Both approaches produce the same set of distinct elements.

Combining multiple vectors

a <- c(1, 2)
b <- c(2, 3)
c <- c(3, 4)

union(union(a, b), c)
# [1] 1 2 3 4

A practical scenario is collecting unique user identifiers from multiple data sources that may have overlapping coverage. If ids1 contains users from one system and ids2 contains users from another, union() gives the complete set without duplicates. This is useful for building a combined allow-list, merging customer databases, or determining the full set of experimental units across separate data-collection channels.

Unique values from multiple sources

# Two data sources with overlapping IDs
ids1 <- c("user_1", "user_2", "user_3")
ids2 <- c("user_3", "user_4", "user_5")

union(ids1, ids2)
# [1] "user_1" "user_2" "user_3" "user_4" "user_5"

union() in practice

union(x, y) returns all unique elements from both vectors combined, removing duplicates. It is equivalent to unique(c(x, y)) but cleaner to read. The result maintains the order: elements from x first (in their original order), then new elements from y that were not already in x.

For finding all unique identifiers across multiple data sources, Reduce(union, list_of_vectors) extends this to more than two vectors at once. This is preferable to chaining union(union(a, b), c) for long lists.

union() is not a symmetric operation in terms of output order, though the resulting set is the same regardless of order. If you need a sorted result, wrap in sort(). For character vectors, case matters, "A" and "a" are treated as distinct elements.

Vectorized set operations in R produce a unique set. union() guarantees no duplicates in the output regardless of how many duplicates exist in the inputs. The function is equivalent to unique(c(x, y)) but more expressive about the intent. In data pipelines, union() is useful for combining allow-lists, valid-value sets, or configuration keys from multiple sources.

For large vectors, R’s set functions are implemented in C and are efficient. However, if you are doing set operations inside a tight loop (for example, updating a set incrementally for each row), consider using an environment as a hash set instead, environments have O(1) name lookup compared to O(n) for vector operations.

Note that union() always de-duplicates, even if a value appears multiple times in x or y, it appears only once in the final result.

union() preserves the relative order of elements, with elements from x appearing before new elements from y. This ordering is deterministic but not alphabetical or numerical — it reflects insertion order, similar to a set implemented as an ordered list. If you need a sorted union, wrap the result with sort(). For large character vectors where membership testing is the bottleneck, building an environment-based hash set manually can be faster than repeated union() calls in a loop, since union() calls unique() internally on each iteration.

Merging allow-lists from multiple configuration files

# Permission lists from three configuration sources
config_dev <- c("alice", "bob", "charlie")
config_staging <- c("bob", "diana", "eve")
config_prod <- c("alice", "diana", "frank")

# Progressive union across environments
dev_staging <- union(config_dev, config_staging)
all_users <- union(dev_staging, config_prod)
all_users
# [1] "alice"   "bob"     "charlie" "diana"   "eve"     "frank"

# Alternative: use Reduce for arbitrary-length lists
configs <- list(config_dev, config_staging, config_prod)
all_users2 <- Reduce(union, configs)
identical(all_users, all_users2)
# [1] TRUE

When building a combined access list from separate environment configurations, union() eliminates duplicates while keeping each user listed once. Chaining two union() calls works for three sources, but Reduce(union, configs) scales to any number of lists without nesting, making it the preferred approach for merging an unknown number of input vectors in production scripts. The result always preserves order: elements from the first vector come first, then new elements from the second, and so on.

See also