12  Lists: Creation, Accessing, and Modification

NoteWhat This Chapter Covers

This chapter introduces R’s second fundamental container: the list. Unlike a vector, a list can hold elements of different types in the same object, numbers next to character strings next to other lists. You will learn how to build a list with list(), how to name its elements, the three styles of accessing list content ([, [[, and $), and the difference between them. You will also see how to replace, add, and remove list elements, how NULL is used to delete an element, and the small helper functions names(), length(), str(), and unlist() that you will reach for repeatedly. By the end of this chapter you will be able to pack any set of heterogeneous values into a list and retrieve them cleanly.

flowchart LR
    B["Build <br> list(...)"] --> L["A List"]
    L --> A["Access <br> [ ], [[ ]], $"]
    L --> M["Modify <br> add, replace, delete (NULL)"]
    L --> I["Inspect <br> names(), length(), str(), unlist()"]
    style B fill:#e3f2fd,stroke:#1976D2
    style L fill:#fff3e0,stroke:#F57C00
    style A fill:#e8f5e9,stroke:#388E3C
    style M fill:#f3e5f5,stroke:#8E24AA
    style I fill:#e8f5e9,stroke:#388E3C


12.1 What Makes a List Different From a Vector?

NoteCore Concept: A List Is a Generic Container

Every element of an atomic vector has the same type. Every element of a list can be a different type, a number, a string, another vector, a data frame, a function, or another list. That flexibility is why lists are how R returns results from modelling functions, holds the results of a statistical test, and represents any nested configuration.

Property Vector List
Elements share a type? Yes. No.
Element can itself be a vector or list? No. Yes (nesting is allowed).
Built with c() list()
Default printing One line, comma separated. Multi-line, each element labelled.

12.2 Creating a List

Notelist() Builds a List; c() Combines Lists

Build a list with the list() function. Arguments can be named or unnamed, and each argument becomes one element.

NoteUnnamed and Mixed-Name Lists
TipBest Practice: Name Your List Elements

A named list is self-documenting. Anyone reading the code can see at a glance what each element represents. Unnamed lists are fine for throwaway interactive work, but for anything that lives in a script, use names.


12.3 Inspecting a List

NoteThe Four Functions You Will Use Most
Function What It Tells You
length(x) Number of top-level elements.
names(x) Character vector of element names (or NULL).
str(x) Compact structural description of every element.
class(x) "list" for a plain list, or a subclass for objects.
TipExpert Insight: str() Is Your Best Friend

When you receive an unfamiliar list from a function, a model fit, a configuration object, an API response, str(x) shows you the entire structure compactly. Make it the first function you run on any new list.


12.4 Accessing List Elements: [, [[, and $

NoteCore Concept: Three Very Different Access Styles

R provides three ways to reach into a list. They look similar and beginners regularly confuse them, but they return fundamentally different things.

Operator Returns Use When
x[i] A sub-list (still a list). You want to keep the list container.
x[[i]] The element itself, unwrapped. You want the value inside.
x$name The element named name, unwrapped (shortcut for x[["name"]]). You want one element by its exact name.
WarningCommon Mistake: student[1] + 1 Fails, student[[1]] + 1 Works

Because x[1] returns a list (not a number), you cannot use it in arithmetic. Use x[[1]] to pull out the underlying value.

NoteSelecting Several Elements

[ is also how you pull multiple elements at once. Pass a vector of names or positions.


12.5 Modifying a List

NoteReplacing an Element in Place

Use [[<- or $<- to replace an element. The old value is discarded and the new value takes its place.

NoteAdding New Elements

Assigning to a name that does not yet exist appends a new element to the list.

NoteRemoving Elements with NULL

Assigning NULL to a list element deletes it. This is a special rule: NULL is not stored inside the list; the whole slot is dropped.

WarningCommon Mistake: Using NULL to Mean “Missing”

Setting student$email <- NULL deletes the element; it does not mark it as missing. If you want to keep the slot but record that the value is unknown, use NA.


12.6 Coercing a List to a Vector: unlist()

NoteFlattening When Types Agree

unlist() collapses a list into a single atomic vector. It works cleanly when every element is atomic and the types can be coerced together. The usual coercion hierarchy applies: if any element is character, the whole result becomes character.

TipExpert Insight: unlist() Is a Coercion, Not a Free Lunch

unlist() is handy for collapsing simple lists, but every time you call it you risk coercion surprises (numbers becoming strings, factors becoming integers). When you care about types, prefer vapply() or purrr::map_*(), which let you assert the expected output type.


12.7 Converting Between List and Vector

Noteas.list() and as.vector()

as.list() wraps each element of a vector into its own list slot. as.vector() strips most attributes from a vector but is rarely interesting on its own.


12.8 A Worked Example: A Student Profile

NoteBuilding, Inspecting, Modifying

Every concept from the chapter shows up once: construction with list(), nested lists for contacts, three access styles ($, [[, $ on a named vector inside the list), reassignment, NULL to delete, and appending a new element.


12.9 Summary

NoteKey Concepts at a Glance
Concept Key Takeaway
Lists are heterogeneous Any types, any nesting, the universal container.
Build with list() Named arguments become element names.
[ vs [[ vs $ [ keeps the list wrapper; [[ and $ return the element itself.
Multi-element selection x[c("a", "b")] and x[1:2] return sub-lists.
Modification $ or [[ on the left-hand side; assigning NULL deletes.
NA vs NULL NA marks a missing value, NULL removes the element entirely.
unlist() Flatten atomic-only lists into a vector; mind the coercion.
str() The single most useful function for exploring an unknown list.
TipApplying This in Practice

Whenever you find yourself wishing a vector could hold a mix of types, a student record, a set of model parameters, the response from a web API, a list is the answer. Get comfortable with the [/[[/$ distinction now; it is the source of more beginner confusion than any other single syntax in R. In the next chapter you will see the operations that run on top of lists: appending, nesting, and the lapply()/sapply() iteration family.