Lists
Reviewed & published by Brayan K
A list is R's flexible container — unlike a vector, it can hold values of different types and even nest other lists, vectors, or data frames inside it.
Part of the free R course at LearnCodingFast — hands-on lessons with worked examples and the output they print, plus practice exercises and a quick quiz.
By the end of this lesson you'll build lists with named elements, access them with $ and [[ ]], modify and extend them, and read nested structures with str().
What You'll Learn in This Lesson
- How a list differs from a vector
- How to create lists with named elements
- How to access elements with $ and [[ ]]
- The difference between [ ] and [[ ]] on lists
- How to add, change, and remove list elements
- How to read nested lists with str()
1️⃣ Creating Lists
Build a list with list(), optionally naming each element with name = value. Elements can be any type — a number, a string, or a whole vector. Access named elements with the $ operator.
# A list can hold DIFFERENT types, unlike a vector. Build it with list().
person <- list(
name = "Ada",
age = 36,
languages = c("R", "Python", "SQL") # an element can be a whole vector
)
person$name # access by name with $ -> [1] "Ada"
person$languages # -> [1] "R" "Python" "SQL"
length(person) # number of elements -> [1] 32️⃣ [[ ]] vs [ ], and Editing
Use [[ ]] (or $) to pull out the actual element; [ ] returns a smaller list. Add or change elements simply by assigning to a name.
# Two bracket styles matter for lists:
# [[ ]] pulls out the ELEMENT itself; [ ] returns a smaller LIST.
scores <- list(math = 90, science = 85, art = 70)
scores[["math"]] # the value -> [1] 90
scores$science # same idea -> [1] 85
scores["math"] # a length-1 LIST, not the number
# Add or change elements by assignment:
scores$music <- 88
names(scores) # -> [1] "math" "science" "art" "music"See how scores["math"] printed as $math followed by the value — that's a list wrapper. scores[["math"]] gave the bare number.
3️⃣ Nested Lists
Lists can contain lists, which lets you model structured records — an order with a customer who has their own fields. Drill in by chaining $ or [[ ]], and use str() to see the whole shape.
# Lists can nest — a list inside a list — which models real records well.
order <- list(
id = 1001,
customer = list(name = "Bob", vip = TRUE),
items = c("pen", "notebook")
)
order$customer$name # drill in with $ -> [1] "Bob"
order[["customer"]][["vip"]] # or [[ ]] -> [1] TRUE
# str() reveals the whole structure at a glance:
str(order)Your turn. Fill in the ___ blank, run it, and compare with the expected output.
# 🎯 YOUR TURN — replace each ___ then run it.
# 1) Build a list describing a book: title, year, and a vector of authors
book <- list(
title = "R Programming",
year = 2023,
authors = ___ # 👉 replace ___ with c("Lee", "Patel")
)
# 2) Print the title and the number of authors
cat("Title:", book$title, "\n")
cat("Authors:", length(book$authors), "\n")
# ✅ Expected output:
# Title: R Programming
# Authors: 2Mini-Challenge: A Movie Record
Write it from the outline, run it, and check it against the example output. Practising add-by-assignment and $ access cements how lists work.
Common Errors (and the fix)
- Getting a list when you wanted the value — x[1] returns a list; use x[[1]] for the element itself.
- "$ operator is invalid for atomic vectors" — you used $ on a vector, not a list. $ only works on lists and data frames.
- Misspelled element name returns NULL — person$naem silently gives NULL. Check names with names(person).
- Removing an element — set it to NULL: person$age <- NULL deletes it from the list.
Pro Tips
- 💡 str() is essential: for any unfamiliar list (or model result), str() shows the structure instantly.
- 💡 Name everything: named elements make $ access readable and self-documenting.
- 💡 Lists power data frames: a data frame is really a list of equal-length columns — the next lessons build on this idea.
📋 Quick Reference — Lists
| Task | R Syntax |
|---|---|
| Create a list | list(a = 1, b = "x") |
| Get an element | lst$a or lst[["a"]] |
| Get a sublist | lst["a"] |
| Add / change | lst$c <- 3 |
| Remove element | lst$a <- NULL |
| Inspect structure | str(lst) |
🎉 Lesson Complete!
- ✅ A list holds mixed types and nested structures
- ✅ Access elements with $ or [[ ]]
- ✅ [ ] returns a sublist; [[ ]] returns the element
- ✅ Add or remove elements by assignment (NULL deletes)
- ✅ str() reveals nested structure
Practice quiz
What is the key difference between a list and a vector?
- A list is always shorter
- A list can hold elements of different types
- A list cannot be named
- A list holds only numbers
Answer: A list can hold elements of different types. Unlike a vector, a list can hold mixed types and even nested structures.
Which function creates a list?
- c()
- vector()
- list()
- data()
Answer: list(). Build a list with list(), optionally naming each element.
What does single-bracket lst["a"] return for a list?
- A smaller list
- The bare element
- An error
- NULL
Answer: A smaller list. Single [ ] returns a sublist; the element stays wrapped in a list.
Which extracts the actual element (the value itself)?
- lst("a")
- lst{"a"}
- lst["a"]
- lst[["a"]]
Answer: lst[["a"]]. Double brackets [[ ]] (or $) reach in and return the element itself.
How do you access a named element with the dollar operator?
- person.name
- person$name
- person->name
- person@name
Answer: person$name. The $ operator accesses a named list element, e.g. person$name.
How do you delete an element from a list?
- Set it to NULL
- Use drop()
- Use remove()
- Set it to NA
Answer: Set it to NULL. Assigning NULL, e.g. lst$a <- NULL, removes the element.
Which function reveals a list's nested structure at a glance?
- dim()
- summary()
- str()
- names()
Answer: str(). str() prints the structure of a list, including nesting.
What happens if you use $ on an atomic vector?
- Returns the first element
- Returns NULL silently
- Works like on a list
- Raises '$ operator is invalid for atomic vectors'
Answer: Raises '$ operator is invalid for atomic vectors'. $ only works on lists and data frames, not atomic vectors.
What does accessing a misspelled list name like person$naem return?
- NULL
- An error
- The first element
- NA
Answer: NULL. A misspelled name silently returns NULL; check names(person).
Why do many R functions return a list?
- Lists print faster
- Lists use less memory
- Lists bundle differently-shaped parts in one object
- Lists are required by R
Answer: Lists bundle differently-shaped parts in one object. A list neatly holds results of different lengths or types, like model output.
Continue this course
- Previous: Control Flow
- Next: Matrices — Two-dimensional numeric data and matrix operations
- Quick reference: R cheat sheet
Frequently asked questions
What is the difference between a list and a vector in R?
A vector can only hold one type — all numbers, or all text, or all logicals. A list is far more flexible: each element can be a different type, and an element can itself be a vector, another list, a data frame, or even a function. Use a vector for a uniform column of values; use a list when you need to bundle together mixed pieces of information, like a record with a name, an age, and a list of hobbies.
When do I use [ ] versus [[ ]] on a list?
Single brackets [ ] return a sublist — a smaller list that still wraps its contents. Double brackets [[ ]] reach in and return the actual element (the value itself). So mylist[1] gives you a one-element list, while mylist[[1]] gives you the thing stored at position 1. As a rule: use [[ ]] (or $) to extract a single element you want to work with, and [ ] when you genuinely want a list back.
How do I access a list element by its name?
If a list has named elements, the cleanest way is the $ operator: person$name. You can also use double brackets with the name in quotes: person[["name"]] — which is handy when the name is stored in a variable. Both return the element itself. The $ form is the most readable for fixed names, while [[ ]] is more flexible for programmatic access.
Why do many R functions return lists?
Lists are the natural container when a result has several differently-shaped parts. For example, fitting a linear model returns a list containing coefficients, residuals, fitted values, and more — each a different length or type. Returning a list lets a function hand back everything in one tidy object that you can then pick apart with $. You'll see this pattern constantly once you start using statistical functions.