R - Lists - Create, Access, Modify
• Lists in R are heterogeneous data structures that can contain elements of different types, including vectors, data frames, functions, and even other lists, making them the most flexible container...
Key Insights
• Lists in R are heterogeneous data structures that can contain elements of different types, including vectors, data frames, functions, and even other lists, making them the most flexible container type in R
• List elements can be accessed using three different operators: [[]] for single element extraction, [] for subsetting that returns a list, and $ for named element access
• Modifying lists involves direct assignment, appending with c() or append(), removing elements by setting to NULL, and using functional approaches like lapply() for transformations across all elements
Creating Lists
The list() function creates lists by combining elements of any type. Unlike vectors that require homogeneous data, lists accept mixed types.
# Basic list creation
employee <- list(
name = "Sarah Chen",
id = 10234,
salary = 85000,
active = TRUE
)
# List with different data structures
mixed_list <- list(
numbers = c(1, 2, 3, 4, 5),
matrix = matrix(1:9, nrow = 3),
dataframe = data.frame(x = 1:3, y = c("a", "b", "c")),
function_obj = mean
)
# Nested lists
project <- list(
name = "API Redesign",
team = list(
lead = "John Doe",
members = c("Alice", "Bob", "Carol"),
size = 4
),
timeline = list(
start = as.Date("2024-01-15"),
end = as.Date("2024-06-30")
)
)
Empty lists serve as containers for dynamic data collection:
# Empty list for later population
results <- list()
# Named empty list with structure
config <- list(
database = NULL,
api_keys = NULL,
settings = NULL
)
Converting other structures to lists uses as.list():
# Vector to list
vec <- c(a = 1, b = 2, c = 3)
vec_list <- as.list(vec)
# Data frame columns to list
df <- data.frame(x = 1:3, y = 4:6)
df_list <- as.list(df) # Each column becomes a list element
Accessing List Elements
Three operators provide different access patterns: [[]] extracts content, [] returns a sublist, and $ accesses named elements.
employee <- list(
name = "Sarah Chen",
id = 10234,
departments = c("Engineering", "DevOps"),
metadata = list(hire_date = "2020-03-15", location = "NYC")
)
# Double bracket - extracts the actual element
name_value <- employee[[1]] # "Sarah Chen"
id_by_name <- employee[["id"]] # 10234
# Single bracket - returns a list
first_element <- employee[1] # list(name = "Sarah Chen")
subset <- employee[c(1, 2)] # list with name and id
# Dollar sign - named access only
dept <- employee$departments # c("Engineering", "DevOps")
Accessing nested list elements requires chaining:
# Multiple levels of extraction
hire_date <- employee[[4]][[1]] # "2020-03-15"
hire_date_alt <- employee$metadata$hire_date # Same result
# Mixed notation
location <- employee[["metadata"]]$location # "NYC"
Accessing multiple elements simultaneously:
# Extract specific elements by index
subset_idx <- employee[c(1, 3)]
# Extract by name
subset_name <- employee[c("name", "departments")]
# Using negative indexing to exclude
without_id <- employee[-2]
Safe access with existence checking:
# Check if element exists
if ("name" %in% names(employee)) {
name <- employee$name
}
# Using get with default
salary <- if (!is.null(employee$salary)) employee$salary else 0
# Programmatic access
field <- "departments"
value <- employee[[field]]
Modifying List Elements
Direct assignment modifies existing elements or adds new ones:
employee <- list(name = "Sarah Chen", id = 10234)
# Modify existing element
employee$name <- "Sarah Chen-Martinez"
employee[[2]] <- 10235
# Add new elements
employee$salary <- 90000
employee[["bonus"]] <- 5000
employee$benefits <- c("health", "dental", "vision")
# Modify nested elements
employee$metadata <- list(updated = Sys.Date())
employee$metadata$updated <- Sys.Date()
Removing elements by setting to NULL:
# Remove single element
employee$bonus <- NULL
# Remove multiple elements (set to list of NULLs)
employee[c("salary", "benefits")] <- list(NULL, NULL)
# Alternative: subset to keep only desired elements
employee <- employee[c("name", "id")]
Appending elements to lists:
# Using c() to combine lists
employee <- c(employee, list(department = "Engineering"))
# append() function with positioning
employee <- append(employee, list(role = "Senior Dev"), after = 2)
# Multiple elements at once
new_fields <- list(
performance = "Excellent",
years_service = 4
)
employee <- c(employee, new_fields)
Bulk modifications with lapply() and related functions:
# Transform all numeric elements
numbers_list <- list(a = 1:5, b = 10:15, c = 20:25)
doubled <- lapply(numbers_list, function(x) x * 2)
# Modify in place with loop
for (name in names(numbers_list)) {
numbers_list[[name]] <- numbers_list[[name]] + 100
}
# Conditional modification
employees <- list(
emp1 = list(name = "Alice", salary = 70000),
emp2 = list(name = "Bob", salary = 65000),
emp3 = list(name = "Carol", salary = 72000)
)
# Apply raise to all
employees <- lapply(employees, function(emp) {
emp$salary <- emp$salary * 1.05
emp
})
Advanced List Operations
Flattening nested lists:
nested <- list(
a = list(1, 2, 3),
b = list(4, 5),
c = 6
)
# Flatten one level
flat <- unlist(nested, recursive = FALSE)
# Completely flatten to vector
completely_flat <- unlist(nested)
# Using do.call for specific flattening
flat_list <- do.call(c, nested)
Merging and combining lists:
list1 <- list(a = 1, b = 2)
list2 <- list(c = 3, d = 4)
list3 <- list(b = 20, e = 5) # Note: 'b' conflicts with list1
# Simple concatenation
combined <- c(list1, list2)
# Merge with override (later values win)
merged <- c(list1, list3) # b = 20
# Using modifyList for recursive merging
base <- list(
config = list(timeout = 30, retries = 3),
enabled = TRUE
)
updates <- list(
config = list(timeout = 60)
)
final <- modifyList(base, updates) # Keeps retries, updates timeout
List comprehension patterns:
# Filter list elements
numbers <- list(a = 5, b = 12, c = 8, d = 15)
filtered <- numbers[sapply(numbers, function(x) x > 10)]
# Map and filter combined
data <- list(
item1 = list(value = 100, active = TRUE),
item2 = list(value = 200, active = FALSE),
item3 = list(value = 150, active = TRUE)
)
active_values <- sapply(
data[sapply(data, function(x) x$active)],
function(x) x$value
)
# Named vector from list
vec <- setNames(
sapply(data, function(x) x$value),
names(data)
)
Working with list attributes:
# Set and retrieve names
my_list <- list(1, 2, 3)
names(my_list) <- c("first", "second", "third")
# Length and structure
len <- length(my_list)
str(my_list)
# Test if object is a list
is.list(my_list) # TRUE
# Get element names
all_names <- names(my_list)