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()"]
    classDef default fill:#2e4057,color:#ffffff,stroke:#ff9933,stroke-width:3px,rx:10px,ry:10px;


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

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.



Summary

Concept Description
Concept and Creation
What Makes a List Different A list is a generic container that can hold any mix of types and structures
list() vs c() list() builds a list; c() combines lists into a flat list
Named vs Unnamed Naming list elements makes code far more readable
Access and Modify
Inspecting with str() str() shows the full structure compactly — your best friend for debugging
Single Bracket [ Single bracket returns a sublist that preserves the list class
Double Bracket [[ Double bracket returns the underlying element directly
Dollar Sign $ Shortcut for accessing named list elements by name
Modifying Elements Assign with [[, $, or [ to add, change, or remove elements