R: R Lists

Last updated: 2026-08-26

In the previous three lessons, we learned about vectors and matrices—both of which require that all elements be of the same type. However, in reality, data is often heterogeneous: student information includes student ID (numbers), name (characters), and grades (vectors). R uses lists to solve the problem of storing heterogeneous data.

Lists are R’s true “Swiss Army knife”—they can hold any type (vectors, matrices, data frames, functions, or even other lists). Data frames data.frame are essentially “lists of vectors of equal length” at their core. This lesson is key to Phase 1.

1. What You'll Learn



2. A Story About Student Information Management

(1) Pain Point: Heterogeneous Data Storage

Ivan works in HR and needs to store the complete information for one employee:

TEXT 📖 Display only
Employee ID:2024001(Numbers)
Name:Alice(Character)
Department:Data Analysis Department(Character)
Salary:15000(Numbers)
Skills:c("R", "Python", "SQL")(Character vector)

If you use a vector, all elements must be of the same type—it won't work. If you use a data frame, each column must have the same number of columns—it won't work either.

(2) Solution Using Lists

R
# One list Fits all types
employee <- list(
  id = 2024001,
  name = "Alice",
  department = "Data Analysis Department",
  salary = 15000,
  skills = c("R", "Python", "SQL"),
  is_active = TRUE
)

# Access any field
employee$name           # [1] "Alice"
employee$skills[1]      # [1] "R"
employee$salary * 12    # [1] 180000  (Annual Salary)

A single list containing 6 fields of different types. That’s the power of R lists.



3. list(): Create a list

(1) Basic Syntax

R
list(key1 = value1, key2 = value2, ...)

(2) 5 Ways to Create

R
# 1. List of Names(Most Commonly Used)
person <- list(name = "Alice", age = 25, scores = c(85, 90, 92))
person
# $name
# [1] "Alice"
# 
# $age
# [1] 25
# 
# $scores
# [1] 85 90 92

# 2. Anonymous List(Browse by Location)
unnamed <- list("a", 1, TRUE)
# [[1]] "a"
# [[2]] 1
# [[3]] TRUE

# 3. Nested Lists
company <- list(
  hr = list(name = "HR", count = 5),
  it = list(name = "IT", count = 20)
)

# 4. Empty list
empty <- list()
length(empty)  # [1] 0

# 5. Use list() to convert vector (Maintain Heterogeneity)
as.list(c(1, 2, 3))
# [[1]] 1
# [[2]] 2
# [[3]] 3

(3) Comparison of Lists and Vectors

100%
graph TB
    A[R Data Structures] --> B[Vector<br/>Homogeneous<br/>One-dimensional]
    A --> C[Matrix<br/>Homogeneous<br/>Two-dimensional]
    A --> D[List<br/>Heterogeneous<br/>Any type]
    A --> E[Data Frame<br/>List of vectors of equal length<br/>Two-dimensional]
    
    style A fill:#fff3cd
    style B fill:#cce5ff
    style C fill:#d4edda
    style D fill:#f8d7da
    style E fill:#e1d4ff
Property Vector c(1, 2, 3) List list(1, "a", TRUE)
Element Type Must Be the Same Can Be Different
Element Type 6 basic types Any R object (including functions and data frames)
Visit v[1] l[[1]]
Variable length ✅ (Can be added to or removed as desired)
Similar to Python tuple dict / list


4. 4 Access Methods (Key Points)

(1) 4 Types of Lookup Tables

Method Syntax Return Purpose
[[i]] The i-th element The element itself Retrieve a single element
[i] The i-th element Sublist Take a subset
$name By name The element itself Get the named element
[["name"]] By name The element itself Equivalent $name

(2) [[ ]] vs [ ]: Key Differences

R
person <- list(name = "Alice", age = 25, scores = c(85, 90, 92))

# [[ ]] Return the element itself
person[[1]]
# [1] "Alice"  ← Character

# [ ] Return to Sublist
person[1]
# $name
# [1] "Alice"  ← Or a list

# Key Differences: Using [ ] after extraction, attempting to access sub-elements may cause an error
person[1]$name  # ✅ $name Still accessible(Syntax sugar)
person[1][1]    # ❌ This is the 1 element,But there is only one child list $name

# Practical Application:Calculate the average score
mean(person[[3]])      # [1] 89  ← [[3]] Extract the vector
mean(person$scores)    # [1] 89  ← $scores Extract the vector
mean(person[3])        # Error: need numeric data
⚠️ Note: Use [[ ]] or $ to access elements, and [ ] to access subsets. This is the most confusing aspect of accessing lists in R.

(3) $ vs [["name"]]

R
# $ is syntactic sugar for [[ "name" ]]
person$name           # Equivalent person[["name"]]
person[["name"]]      # Equivalent person$name

# $ Supports partial matches(Not recommended)
person$na             # Automatically matched to "name"
# [1] "Alice"

# [[ ]] Partial matches are not supported(Safer)
person[["na"]]        # Error
💡 Tip: Use [[ "name" ]] for production code—it’s clear and unambiguous, and doesn’t rely on partial matches.

(4) Accessing Nested Lists

R
company <- list(
  hr = list(name = "HR", count = 5, head = "Alice"),
  it = list(name = "IT", count = 20, head = "Bob")
)

# Accessing Nested Elements
company$hr$head                  # [1] "Alice"
company[["it"]][["head"]]        # [1] "Bob"
company$it$count                 # [1] 20


5. Modifying, Adding, and Deleting Items in a List

(1) Modify an element

R
person <- list(name = "Alice", age = 25)

# Edit ([[ ]] or $ both can be changed)
person$age <- 26
person[["age"]]
# [1] 26

# Batch Edit
person$age <- person$age + 1
person$age  # [1] 27

(2) Add an element

R
# Methods 1: Use $ to add
person$email <- "alice@example.com"

# Methods 2: Use [[ ]] to add
person[["phone"]] <- "13800000000"

# Methods 3: Use c() to merge
person <- c(person, address = "Haidian District, Beijing")

(3) Deleting Elements

R
# Assign NULL to delete
person$phone <- NULL

# Or
person[["address"]] <- NULL

(4) View the list structure

R
# View Structure(Important Debugging Functions)
str(person)
# List of 4
#  $ name    : chr "Alice"
#  $ age     : num 27
#  $ email   : chr "alice@example.com"
#  $ address : chr "Haidian District, Beijing"

# View All Names
names(person)
# [1] "name" "age" "email" "address"


6. lapply(): Batch Processing of Lists

(1) Why use lapply?

The element types in a list can vary—you cannot simply use sum() mean(). The lapply function allows us to apply a function to each element:

R
mixed_list <- list(
  numbers = 1:5,
  chars = c("a", "b", "c"),
  logical = c(TRUE, FALSE, TRUE)
)

# Calculate the length of each element
lapply(mixed_list, length)
# $numbers
# [1] 5
# 
# $chars
# [1] 3
# 
# $logical
# [1] 3

(2) lapply + Custom Function

R
# CalculateFor each element,"Abstract"
summary_list <- lapply(mixed_list, function(x) {
  if (is.numeric(x)) {
    return(list(mean = mean(x), sum = sum(x)))
  } else {
    return(list(length = length(x), class = class(x)))
  }
})

summary_list$numbers
# $mean
# [1] 3
# 
# $sum
# [1] 15

(3) Simplifying the results with sapply()

R
# lapply Back to List,sapply Simplify to a vector
sapply(mixed_list, length)
# numbers    chars  logical 
#       5        3        3


7. Merging Lists: c() and unlist()

(1) c() Merge Lists

R
# Merge Multiple Lists
list1 <- list(a = 1, b = 2)
list2 <- list(c = 3, d = 4)

merged <- c(list1, list2)
merged
# $a 1
# $b 2
# $c 3
# $d 4

(2) unlist() flattens to a vector

R
# Flatten the list into a vector(Only for"Atom"Element Valid)
numbers_list <- list(a = 1, b = 2, c = 3)
unlist(numbers_list)
# a b c
# 1 2 3

# Note:The type will be coerced after flattening.
mixed <- list(1, "a", TRUE)
unlist(mixed)
# [1] "1"    "a"    "TRUE"  ← Convert All to Strings


8. list2env(): Convert a list to an environment

Turn each element in the list into a global variable:

R
config <- list(
  host = "localhost",
  port = 3306,
  user = "root",
  password = "secret"
)

# Convert list elements to environment variables
list2env(config, envir = .GlobalEnv)

# You can use it right away
host    # [1] "localhost"
port    # [1] 3306
⚠️ Note: list2env() can pollute the global environment, so use with caution. It is most commonly used in scenarios where configurations are loaded from YAML or JSON.



9. Comparison of Common Operations

(1) c() vs list() vs unlist()

Function Purpose Input → Output
c() Combining Atomic Elements c(1, 2, 3) → Vectors
list() Create a heterogeneous container list(1, "a") → List
unlist() Flatten the list into a vector unlist(list(1,2,3)) → Vector

(2) Converting a List to a DataFrame

R
# A list of vectors of equal length can be converted data.frame
df <- as.data.frame(list(
  name = c("A", "B", "C"),
  age = c(20, 25, 30),
  score = c(85, 90, 78)
))
print(df)
#   name age score
# 1    A  20    85
# 2    B  25    90
# 3    C  30    78
💡 Tip: A data frame is essentially a "list of vectors of equal length"—we'll explore this in more depth in the next lesson.



10. Complete Example: Employee Record Management

Below is an example of a complete workflow that ties together all the list features covered in this lesson.

▶ Example: Employee File Management

R 📖 Display only
# ============================================
# Employee File Management
# Features:Manage with Lists 3 Complete information about each employee
# ============================================

# 1. Create 3 List of employees
employees <- list(
  alice = list(
    id = 2024001,
    name = "Alice",
    department = "Data Analysis Department",
    salary = 15000,
    skills = c("R", "Python", "SQL"),
    projects = c("User Profiles", "Sales Forecast")
  ),
  bob = list(
    id = 2024002,
    name = "Bob",
    department = "Engineering Department",
    salary = 18000,
    skills = c("Java", "Go", "Docker"),
    projects = c("API Refactoring", "Microservice Migration")
  ),
  charlie = list(
    id = 2024003,
    name = "Charlie",
    department = "Product Department",
    salary = 20000,
    skills = c("Figma", "Axure"),
    projects = c("V2.0 Design", "User Research")
  )
)

# 2. View a specific employee's full profile
cat("=== Alice Full Information ===\n")
str(employees$alice)
# List of 6
#  $ id         : num 2024001
#  $ name       : chr "Alice"
#  $ department : chr "Data Analysis Department"
#  $ salary     : num 15000
#  $ skills     : chr [1:3] "R" "Python" "SQL"
#  $ projects   : chr [1:2] "User Profiles" "Sales Forecast"

# 3. Use sapply for batch processing
cat("\n=== Annual Salaries of All Employees ===\n")
annual_salaries <- sapply(employees, function(emp) emp$salary * 12)
print(annual_salaries)
#    alice      bob  charlie 
#   180000   216000   240000

# 4. Use sapply to count skills
cat("\n=== Number of skills per employee ===\n")
skill_counts <- sapply(employees, function(emp) length(emp$skills))
print(skill_counts)
#    alice      bob  charlie 
#        3         3         2

# 5. Use split for grouping by department
cat("\n=== Grouped by Department ===\n")
dept_groups <- split(
  sapply(employees, function(emp) emp$name),
  sapply(employees, function(emp) emp$department)
)
print(dept_groups)
# $Data Analysis Department
# [1] "Alice"
# 
# $Engineering Department
# [1] "Bob"
# 
# $Product Department
# [1] "Charlie"

# 6. Add a New Employee
employees$diana <- list(
  id = 2024004,
  name = "Diana",
  department = "Data Analysis Department",
  salary = 16000,
  skills = c("R", "Tableau"),
  projects = c("Data Visualization")
)

cat("\n=== Add Diana after ===\n")
cat("Total Number of Employees:", length(employees), "\n")
cat("Diana Department:", employees$diana$department, "\n")

# 7. Identify the employee with the highest salary
top_salary <- which.max(sapply(employees, function(emp) emp$salary))
cat("\nHighest Salary:", names(employees)[top_salary], "(", max(sapply(employees, function(emp) emp$salary)), "USD)\n")

# 8. Retrieve the skills of all employees(Merge all skill vectors)
all_skills <- unlist(lapply(employees, function(emp) emp$skills))
cat("\n=== Company-wide Skills Summary ===\n")
print(table(all_skills))
# all_skills
#   Axure  Docker    Figma      Go    Java   Python       R     SQL Tableau 
#       1       1       1       1       1       1       2       1       1
56 logic lines (exceeds 40-line limit, display only)

Expected Output (Excerpt):

TEXT 📖 Display only
=== Alice Full Information ===
List of 6
 $ id         : num 2024001
 $ name       : chr "Alice"
 ...

=== Annual Salaries of All Employees ===
   alice      bob  charlie 
  180000   216000   240000 

=== Company-wide Skills Summary ===
all_skills
  Axure  Docker  Figma      Go   Java  Python     R   SQL Tableau 
      1       1       1       1       1       1       2       1       1 

❓ FAQ

Q What is the difference between [[ ]] and [ ]?
A [[ ]] returns the element itself (which can be further manipulated), while [ ] returns a sublist (whose elements cannot be further manipulated). Use [[ ]] or $ to retrieve elements, and [ ] to retrieve subsets. This is one of the most confusing aspects of R syntax.
Q Are $ and [[ "name" ]] the same?
A They are essentially equivalent. However, $ supports partial matching ($na automatically matches "name"), while [[ ]] does not. It is safer to use [[ "name" ]] in production code.
Q What is the relationship between lists and vectors?
A Vectors are homogeneous and one-dimensional (all elements are of the same type), while lists are heterogeneous and can be of any dimension (elements can be of any type). data.frame At the core, a list is a "list of vectors of equal length"—understanding lists means understanding data frames.
Q How do I choose between lapply() and sapply()?
A lapply() returns a list (preserving the structure), while sapply() simplifies the result to a vector or matrix (making it easier to read). Use sapply for everyday tasks, and lapply for functional programming pipelines.
Q unlist() When should it be used?
A To "flatten" a list into a vector—but only atomic elements can be flattened. Mixed types will be coerced (e.g., list(1, "a") → c("1", "a")).

📖 Summary


📝 Exercises

  1. Basic Exercise: Create a list named student containing four fields: name (character), age (number), scores (vector of values c(85, 90, 92)), and passed (boolean). Print the list structure (str()), and access name and scores[2].

  2. Basic Problem: Create a nested list family containing three sublists father, mother, and child (each of which contains name and age). Access child$age and print it.

  3. Basic Problem: Use lapply() to batch-calculate the "annual salary" (salary * 12) for a list of three employees (which you construct yourself), and use sapply() to simplify the results into a vector output.

  4. Advanced Problem: Write a function create_employee(name, dept, salary) that returns a named list (containing id, name, dept, salary, skills=empty vector, is_active=TRUE). Call it three times to create three employees and store them in a large list. Use sapply() to extract the "annual salary" for all employees.

  5. Challenge: Write a script to simulate "library" management: ① Create a list library containing 3 books (each book has 4 fields: title, author, year, and available); ② Use lapply to set the available value for all books to FALSE (checked out); ③ Use sapply to find all books available for checkout (available == TRUE); ④ Add a new book to the list.

Web-Tutorial.com

Web-Tutorial Tech Team

A team of developers maintaining programming tutorials. Each tutorial is written and reviewed by developers with expertise in that field. We work to keep our content accurate and reliable — if you spot an issue, please let us know.

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏