## 3.5 Lists

Lists are a step up in complexity from atomic vectors: each element can be any type, not just vectors. Technically speaking, each element of a list is actually the same type because, as you saw in Section 2.3.3, each element is really a reference to another object, which can be any type.

### 3.5.1 Creating

You construct lists with list():

l1 <- list(
1:3,
"a",
c(TRUE, FALSE, TRUE),
c(2.3, 5.9)
)

typeof(l1)
#>  "list"

str(l1)
#> List of 4
#>  $: int [1:3] 1 2 3 #>$ : chr "a"
#>  $: logi [1:3] TRUE FALSE TRUE #>$ : num [1:2] 2.3 5.9

Because the elements of a list are references, creating a list does not involve copying the components into the list. For this reason, the total size of a list might be smaller than you might expect.

lobstr::obj_size(mtcars)
#> 7,208 B

l2 <- list(mtcars, mtcars, mtcars, mtcars)
lobstr::obj_size(l2)
#> 7,288 B

Lists can contain complex objects so it’s not possible to pick a single visual style that works for every list. Generally I’ll draw lists like vectors, using colour to remind you of the hierarchy. Lists are sometimes called recursive vectors because a list can contain other lists. This makes them fundamentally different from atomic vectors.

l3 <- list(list(list(1)))
str(l3)
#> List of 1
#>  $:List of 1 #> ..$ :List of 1
#>   .. ..$: num 1 c() will combine several lists into one. If given a combination of atomic vectors and lists, c() will coerce the vectors to lists before combining them. Compare the results of list() and c(): l4 <- list(list(1, 2), c(3, 4)) l5 <- c(list(1, 2), c(3, 4)) str(l4) #> List of 2 #>$ :List of 2
#>   ..$: num 1 #> ..$ : num 2
#>  $: num [1:2] 3 4 str(l5) #> List of 4 #>$ : num 1
#>  $: num 2 #>$ : num 3
#>  \$ : num 4 ### 3.5.2 Testing and coercion

The typeof() a list is list. You can test for a list with is.list(), and coerce to a list with as.list().

list(1:3)
#> []
#>  1 2 3
as.list(1:3)
#> []
#>  1
#>
#> []
#>  2
#>
#> []
#>  3

You can turn a list into an atomic vector with unlist(). The rules for the resulting type are complex, not well documented, and not always equivalent to what you’d get with c().

### 3.5.3 Matrices and arrays

With atomic vectors, the dimension attribute is commonly used to create matrices. With lists, the dimension attribute can be used to create list-matrices or list-arrays:

l <- list(1:3, "a", TRUE, 1.0)
dim(l) <- c(2, 2)
l
#>      [,1]      [,2]
#> [1,] Integer,3 TRUE
#> [2,] "a"       1

l[[1, 1]]
#>  1 2 3

These data structures are relatively esoteric but they can be useful if you want to arrange objects in a grid-like structure. For example, if you’re running models on a spatio-temporal grid, it might be more intuitive to store the models in a 3D array that matches the grid structure.

### 3.5.4 Exercises

1. List all the ways that a list differs from an atomic vector.

2. Why do you need to use unlist() to convert a list to an atomic vector? Why doesn’t as.vector() work?

3. Compare and contrast c() and unlist() when combining a date and date-time into a single vector.