A List Holds Anything
A vector has one hard rule: every element must be the same type. A list has no such rule. Each element of a list can be a different type, a different length, even another list. It's R's general-purpose container:
length() reports 4 - four elements, even though one of them (scores) contains three numbers itself. A list counts its compartments, not what's inside them. str() (structure) is the fastest way to see what a list actually holds - one line per element with its name, type, and a preview. Make str() a reflex: whenever a function hands you something and you're not sure what it is, str() it.
Names are optional (list(1, "a", TRUE) is legal) but almost always worth having - a named list documents itself.
The Three Ways In: [, [[ and $
This is the part of lists that everyone gets wrong once, so let's get it exactly right. There are three access operators, and they do two different jobs:
[returns a smaller list - a container of the same kind, holding whatever you selected.[[returns the element itself - the actual value inside the compartment.$is shorthand for[[with a literal name:person$ageisperson[["age"]].
The difference is invisible when you print casually and very visible the moment you try to compute:
person["scores"] has class "list" - a list of length 1 with the scores vector inside it. person[["scores"]] has class "numeric" - the vector itself, ready for mean(). Call mean() on the single-bracket version and you get an error about the argument not being numeric; that error message is almost always a sign you used [ where you meant [[.
A metaphor that sticks: a list is a train. [ gives you a shorter train - still a train. [[ opens a carriage and hands you the cargo.
So when is [ the right tool? When you want several elements and want to keep them packaged:
You can't pull two elements "themselves" at once - two values need a container - so multi-element selection is always single-bracket, and always yields a list.
One more difference: $ uses partial matching (person$sc finds scores if it's unambiguous), which is convenient in the console and a liability in scripts. In code that has to keep working, prefer [[ with the full name - it also accepts a name stored in a variable, which $ can't do.
Adding, Changing, Removing
Lists grow and shrink by plain assignment - no special append method needed:
- Assigning to a name that doesn't exist yet adds the element.
- Assigning to an existing name replaces it.
- Assigning to position
length(x) + 1appends an unnamed element. - Assigning
NULLremoves the element entirely - the list gets shorter. (This is the one placeNULLacts as a deletion command; if you genuinely need to store "nothing" in a compartment, usex["k"] <- list(NULL).)
To glue two lists end to end, c() works on lists just as it does on vectors: c(list_a, list_b) returns one longer list.
Nested Lists
Because a list element can be another list, lists nest to any depth - which makes them R's natural shape for structured data like parsed JSON or grouped results:
Drilling in is just chaining accessors - each $ or [[ steps one level deeper. When a nested structure gets confusing, str(company) shows the whole tree at once, and str(company, max.level = 1) shows just the top layer.
Flattening with unlist()
unlist() collapses a list - however deeply nested - into a single vector:
Two things to notice. First, unlist() builds names for you (math1, math2, art) so you can still tell where each value came from. Second - and this is the trap - a vector holds only one type, so unlisting a mixed list coerces everything to the most flexible type present. The number 1 came back as the string "1". If you unlist and your numbers turn into text, the list wasn't as uniform as you thought.
Why Lists Matter
Lists can feel like a beginner topic you'll outgrow. It's the opposite - they're load-bearing across the whole language:
- Functions that return several things return a list. R functions return one object; a list makes that one object carry a fitted value here, a coefficient table there. When you run a regression, the model object you get back is a (large, classed) list -
str()on it proves it. - A data frame is a list of columns - equal-length vectors with some extra behavior on top. Everything you just learned (
$,[[, assigningNULLto drop an element) applies verbatim to data frame columns. - The apply family -
lapply()and friends - takes a list, applies a function to each element, and hands back a list. Lists in, lists out is the standard shape of repeated computation in R.
What You Take Away
- Lists hold anything: mixed types, unequal lengths, other lists, whole data frames.
[returns a smaller list;[[returns the element itself;$is[[with a literal name. Reaching for math? You want[[or$.- Add by assigning to a new name, remove by assigning
NULL, combine withc(). unlist()flattens to a vector and coerces mixed types on the way - check the result's class.- Data frames are lists of columns, so this knowledge transfers directly.
Next up: matrices - the all-one-type, rows-and-columns counterpart to the list.
Frequently Asked Questions
What is the difference between [ and [[ in R?
Single brackets [ always return a list containing the selected elements - a smaller container of the same kind. Double brackets [[ reach inside and return the element itself. If x$scores holds a numeric vector, x["scores"] is a list of length 1 and x[["scores"]] is the numeric vector. Use [[ (or $) when you want to actually work with the value.
How do you add an element to a list in R?
Assign to a name that doesn't exist yet: x$email <- "rosa@example.com" or x[["email"]] <- .... The list grows automatically. To append without a name, assign to the next position: x[[length(x) + 1]] <- value. To remove an element, assign NULL to it: x$email <- NULL.
How do you convert a list to a vector in R?
unlist(x) flattens a list (including nested ones) into a single vector. Because a vector holds only one type, everything gets coerced to the most flexible type present - a list of numbers and strings becomes an all-character vector. Check the result's class() after unlisting.