R Lists Exercises: 10 Practice Problems with Full Solutions
Practice R lists with 10 exercises covering creation, access ($ vs [[]] vs []), modification, nested lists, lapply/sapply, and extracting model results. Each problem has interactive code and a solution.
Lists are R's most flexible structure. These exercises test your understanding of how they differ from vectors and data frames, how to navigate nested structures, and how to iterate over list elements.
Easy (1-4): Create and Access
Exercise 1: Build a Student Record
Create a list representing a student: name, age, courses (a character vector of 3 courses), grades (a named numeric vector), and graduated (logical). Print the student's name and their Math grade.
Given a list, extract the scores element three ways and show what type each returns. Then demonstrate why [] fails with mean() but [[]] works.
# Exercise 2: Show the difference between [], [[]], and $
data <- list(scores = c(88, 92, 75, 95), name = "Test Results")
# Extract scores using all three methods
# Show the type of each result
# Demonstrate: mean works with [[]] but not []
Click to reveal solution
data <- list(scores = c(88, 92, 75, 95), name = "Test Results")
# Three extraction methods
with_single <- data["scores"] # Returns a LIST
with_double <- data[["scores"]] # Returns the VECTOR
with_dollar <- data$scores # Returns the VECTOR
cat("data['scores'] type:", class(with_single), "\n")
cat("data[['scores']] type:", class(with_double), "\n")
cat("data$scores type:", class(with_dollar), "\n\n")
# mean works with [[ ]] and $ but not [ ]
cat("mean(data[['scores']]):", mean(with_double), "\n")
cat("mean(data$scores):", mean(with_dollar), "\n")
# This would fail:
tryCatch(
mean(data["scores"]),
warning = function(w) cat("mean(data['scores']) WARNING:", w$message, "\n")
)
Key concept:[] returns a sub-list. [[]] and $ return the element itself. You can't compute mean() on a list — you need the vector inside.
Exercise 3: Add and Remove Elements
Start with config <- list(host = "localhost", port = 8080). Add a database element, change port to 5432, and remove host. Show the list after each step.
# Exercise 3: Modify a list step by step
config <- list(host = "localhost", port = 8080)
Key concept: Chain $ to drill into nested lists. sapply() iterates over list elements and simplifies the result to a vector.
Medium (5-7): Iteration
Exercise 5: lapply vs sapply
Given a list of numeric vectors (different lengths), compute the mean of each using lapply and sapply. Show how the results differ.
# Exercise 5: lapply vs sapply
datasets <- list(
exam1 = c(88, 72, 95, 61, 83),
exam2 = c(90, 85, 78, 92),
exam3 = c(67, 73, 81, 77, 88, 92, 65)
)
# Compute means using both lapply and sapply
# Show the type and structure of each result
Click to reveal solution
datasets <- list(
exam1 = c(88, 72, 95, 61, 83),
exam2 = c(90, 85, 78, 92),
exam3 = c(67, 73, 81, 77, 88, 92, 65)
)
# lapply returns a LIST
means_list <- lapply(datasets, mean)
cat("lapply result type:", class(means_list), "\n")
str(means_list)
# sapply returns a VECTOR (when possible)
means_vec <- sapply(datasets, mean)
cat("\nsapply result type:", class(means_vec), "\n")
cat("Values:", means_vec, "\n")
# sapply with a function that returns multiple values → matrix
stats <- sapply(datasets, function(x) c(mean = mean(x), sd = round(sd(x), 1), n = length(x)))
cat("\nMultiple values → matrix:\n")
print(stats)
Key concept:lapply() always returns a list (predictable). sapply() simplifies: single values → vector, multiple values → matrix. Use lapply() in functions for safety.
Exercise 6: Build a List in a Loop
Run 5 simulations, each generating 100 random numbers with a different mean (10, 20, 30, 40, 50). Store each result in a named list, then summarize.
# Exercise 6: Simulation results in a list
# 5 simulations, different means, store in a list
Click to reveal solution
set.seed(42)
results <- list()
for (i in 1:5) {
sim_mean <- i * 10
data <- rnorm(100, mean = sim_mean, sd = 5)
results[[paste0("sim_", i)]] <- list(
target_mean = sim_mean,
actual_mean = round(mean(data), 2),
sd = round(sd(data), 2),
data = data
)
}
# Summarize
cat("Simulation results:\n")
for (name in names(results)) {
r <- results[[name]]
cat(sprintf(" %s: target=%d, actual=%.2f, sd=%.2f\n",
name, r$target_mean, r$actual_mean, r$sd))
}
# Compare target vs actual means
targets <- sapply(results, function(r) r$target_mean)
actuals <- sapply(results, function(r) r$actual_mean)
cat("\nMax deviation:", round(max(abs(targets - actuals)), 2), "\n")
Key concept: Use results[[name]] <- value to add named elements in a loop. Each element can be a complex sub-list.
Exercise 7: Transform a List of Data Frames
Given a list of data frames (one per city), add a city column to each and combine them into one data frame.
# Exercise 7: Combine list of data frames
city_data <- list(
NYC = data.frame(month = 1:3, temp = c(32, 35, 45), rain = c(3.5, 3.0, 4.2)),
LA = data.frame(month = 1:3, temp = c(58, 60, 63), rain = c(3.1, 3.5, 2.4)),
Chicago = data.frame(month = 1:3, temp = c(25, 29, 40), rain = c(1.8, 1.5, 2.7))
)
# 1. Add a 'city' column to each data frame
# 2. Combine into one data frame
# 3. Find the coldest city in month 1
Click to reveal solution
city_data <- list(
NYC = data.frame(month = 1:3, temp = c(32, 35, 45), rain = c(3.5, 3.0, 4.2)),
LA = data.frame(month = 1:3, temp = c(58, 60, 63), rain = c(3.1, 3.5, 2.4)),
Chicago = data.frame(month = 1:3, temp = c(25, 29, 40), rain = c(1.8, 1.5, 2.7))
)
# 1. Add city column using lapply
city_data <- lapply(names(city_data), function(city) {
df <- city_data[[city]]
df$city <- city
df
})
# 2. Combine into one data frame
combined <- do.call(rbind, city_data)
cat("Combined data:\n")
print(combined)
# 3. Coldest city in month 1
month1 <- combined[combined$month == 1, ]
coldest <- month1[which.min(month1$temp), ]
cat("\nColdest in month 1:", coldest$city, "at", coldest$temp, "\u00b0F\n")
Key concept:lapply() over names() lets you modify each data frame while keeping track of its name. do.call(rbind, list_of_dfs) stacks them into one data frame.
Hard (8-10): Real-World Patterns
Exercise 8: Extract Model Results
Fit linear models for each cylinder group in mtcars and extract key results.
# Exercise 8: Model results from a list
# Split mtcars by cyl, fit lm(mpg ~ wt) for each group
# Extract R-squared and wt coefficient from each model
Click to reveal solution
# Split data by cylinders
groups <- split(mtcars, mtcars$cyl)
cat("Groups:", names(groups), "\n\n")
# Fit a model for each group
models <- lapply(groups, function(df) lm(mpg ~ wt, data = df))
# Extract results
results <- sapply(names(models), function(name) {
s <- summary(models[[name]])
c(n = nrow(groups[[name]]),
r_squared = round(s$r.squared, 3),
wt_coef = round(coef(models[[name]])["wt"], 2),
p_value = round(s$coefficients["wt", "Pr(>|t|)"], 4))
})
cat("Model results by cylinder group:\n")
print(results)
Key concept:split() creates a list of data frames by group. lapply() fits a model to each. sapply() extracts results into a matrix. This split-apply-combine pattern is fundamental to data analysis.
Exercise 9: JSON-like Nested Structure
Parse and analyze a nested list representing API response data.
# Exercise 9: Work with API-style nested data
api_response <- list(
status = "success",
data = list(
users = list(
list(id = 1, name = "Alice", scores = c(88, 92, 75)),
list(id = 2, name = "Bob", scores = c(95, 80, 85)),
list(id = 3, name = "Carol", scores = c(72, 68, 90))
),
total = 3
)
)
# 1. Extract all user names into a vector
# 2. Calculate each user's average score
# 3. Find the user with the highest average
Click to reveal solution
api_response <- list(
status = "success",
data = list(
users = list(
list(id = 1, name = "Alice", scores = c(88, 92, 75)),
list(id = 2, name = "Bob", scores = c(95, 80, 85)),
list(id = 3, name = "Carol", scores = c(72, 68, 90))
),
total = 3
)
)
users <- api_response$data$users
# 1. All names
names_vec <- sapply(users, function(u) u$name)
cat("Users:", names_vec, "\n")
# 2. Average scores
avg_scores <- sapply(users, function(u) round(mean(u$scores), 1))
names(avg_scores) <- names_vec
cat("Averages:", avg_scores, "\n")
# 3. Highest average
best <- names_vec[which.max(avg_scores)]
cat("Top performer:", best, "with", max(avg_scores), "\n")
Key concept: API responses and JSON data are naturally represented as nested lists in R. sapply() with anonymous functions extracts values from each nested element.
Exercise 10: Configuration Manager
Write a function that merges two configuration lists, where the second list overrides values from the first (like merging default + user config).
# Exercise 10: Merge configuration lists
defaults <- list(
host = "localhost",
port = 8080,
debug = FALSE,
timeout = 30,
database = list(name = "mydb", pool_size = 5)
)
user_config <- list(
port = 5432,
debug = TRUE,
database = list(name = "production")
)
# Write merge_config(defaults, overrides) that:
# - Keeps all defaults
# - Overrides with user_config values where they exist
# - Result should have: host=localhost, port=5432, debug=TRUE, timeout=30,
# database=list(name="production", pool_size=5)
Click to reveal solution
merge_config <- function(defaults, overrides) {
result <- defaults
for (key in names(overrides)) {
if (is.list(defaults[[key]]) && is.list(overrides[[key]])) {
# Recursively merge nested lists
result[[key]] <- merge_config(defaults[[key]], overrides[[key]])
} else {
result[[key]] <- overrides[[key]]
}
}
return(result)
}
defaults <- list(
host = "localhost", port = 8080, debug = FALSE,
timeout = 30, database = list(name = "mydb", pool_size = 5)
)
user_config <- list(
port = 5432, debug = TRUE,
database = list(name = "production")
)
final <- merge_config(defaults, user_config)
cat("Final config:\n")
str(final)
# Verify: database should have BOTH name (overridden) and pool_size (from default)
cat("\nDB name:", final$database$name, "(overridden)\n")
cat("DB pool:", final$database$pool_size, "(from default)\n")
Key concept: Recursive functions can process nested lists. The function checks if both the default and override values are lists — if so, it merges recursively. Otherwise, the override replaces the default.
Summary: Skills Practiced
Exercises
List Skills
1-4 (Easy)
Create, $/[[]]/[] access, modify, nested access
5-7 (Medium)
lapply/sapply, loops with lists, do.call(rbind)
8-10 (Hard)
Model results, API-like data, recursive merge
What's Next?
More exercise sets:
R Control Flow Exercises — if/else and loop practice
R Functions Exercises — write, debug, and optimize
R apply Family Exercises — master apply, lapply, sapply, tapply