R Basics - Lists

Lists are a little different to other data structures you typically encounter in R. They can contain elements of different types, including other lists, and offer a large amount of flexibility.

Lists can be created with list() and may be returned by some functions such as lapply().

Creating a list

The list() function is the most common way to create a list object.

> my_list <- list("string", 1:5, list("string", 1:5), c(TRUE, FALSE))

> print(my_list)
#' [[1]]
#' [1] "string"
#' 
#' [[2]]
#' [1] 1 2 3 4 5
#' 
#' [[3]]
#' [[3]][[1]]
#' [1] "string"
#' 
#' [[3]][[2]]
#' [1] 1 2 3 4 5
#' 
#' 
#' [[4]]
#' [1]  TRUE FALSE

Accessing list elements

Our list object my_list contains a character vector, a numeric vector, a list, and a logical vector.

We can access elements within our list using double square brackets [[ ]]. To access the first element of my_list we would use [[1]]. As with all indexing in R, the index for list objects starts at 1.

> print(my_list[[1]])
#' [1] "string"

The 2nd element of my_list is a numeric vector, we can interact with it like any other vector by combining the list subsetting with a vector subset. We could use this approach to return the 2nd and 3rd element of the vector.

> print(my_list[[2]][2:3])
#' [1] 2 3

We can also perform actions over list elements.

# sum the entire vector
> print(sum(my_list[[2]]))
#' [1] 15

# sum a subset of the vector
> print(sum(my_list[[2]][2:3]))
#' [1] 5

The 3rd element of my_list is a list itself, which we can refer to as a nested list. To access an element of the nested list we need to utilise the [[ ]] syntax twice.

# access the 3rd element of my_list
> print(my_list[[3]])
#' [[1]]
#' [1] "string"
#' 
#' [[2]]
#' [1] 1 2 3 4 5

# access the 1st element of the nested list
> print(my_list[[3]][[1]])
#' [1] "string"

# subset the 2nd element of the nested list
>  print(my_list[[3]][[2]][2:3])
#' [1] 2 3

Named lists

We can also create named lists. Notice that we don’t wrap the list element names in quotations (" ").

> my_list <- list(
    string_vector = "string",
    numeric_vector = 1:5,
    list_object = list("string", 1:5), 
    logical_vector = c(TRUE, FALSE)
    )

> print(my_list)
#' $string_vector
#' [1] "string"
#' 
#' $numeric_vector
#' [1] 1 2 3 4 5
#' 
#' $list_object
#' $list_object[[1]]
#' [1] "string"
#' 
#' $list_object[[2]]
#' [1] 1 2 3 4 5
#' 
#' 
#' $logical_vector
#' [1]  TRUE FALSE

We can now access the elements of my_list using the name and $ operator, though the index system still works.

> print(my_list$string_vector)
#' [1] "string"

> identical(my_list$string_vector, my_list[[1]])
#' [1] TRUE

We can also apply names to an existing list with the names() function.

> my_list <- list("string", 1:5, list("string", 1:5), c(TRUE, FALSE))

> names(my_list) = c("string_vector", "numeric_vector", "list_object", "logical_vector")

> print(my_list)
#' $string_vector
#' [1] "string"
#' 
#' $numeric_vector
#' [1] 1 2 3 4 5
#' 
#' $list_object
#' $list_object[[1]]
#' [1] "string"
#' 
#' $list_object[[2]]
#' [1] 1 2 3 4 5
#' 
#' $logical_vector
#' [1]  TRUE FALSE

Appending to a list

append()

We can add to a list using append(). Lets append a data.frame object to my_list.

> print(append(my_list, list(df_object = data.frame(a = 1:5, b = 6:10)))
#' $string_vector
#' [1] "string"
#' 
#' $numeric_vector
#' [1] 1 2 3 4 5
#' 
#' $list_object
#' $list_object[[1]]
#' [1] "string"
#' 
#' $list_object[[2]]
#' [1] 1 2 3 4 5
#' 
#' 
#' $logical_vector
#' [1]  TRUE FALSE
#' 
#' $df_object
#'   a  b
#' 1 1  6
#' 2 2  7
#' 3 3  8
#' 4 4  9
#' 5 5 10

Information: If you are coming to R from another language such as python, you need to be aware that append() does not take care of assignment and doesn't modify my_list in place, so you do need to assign the output of append() to store it (i.e. my_list <- append(...).

You should also be aware that with an object such as a data.frame, you need to ensure it is within a list itself when being appended to an existing list. If we omit this step then the individual columns will be appended rather than the data.frame as a whole. This is because beneath the surface, data.frames are actually list objects.

> print(append(my_list, data.frame(a = 1:5, b = 6:10))
#'$string_vector
#'[1] "string"
#'
#'$numeric_vector
#'[1] 1 2 3 4 5
#'
#'$list_object
#'$list_object[[1]]
#'[1] "string"
#'
#'$list_object[[2]]
#'[1] 1 2 3 4 5
#'
#'
#'$logical_vector
#'[1]  TRUE FALSE
#'
#'$a
#'[1] 1 2 3 4 5
#'
#'$b
#'[1]  6  7  8  9 10

c()

We can also append to a list with c().

>  my_list <- c(my_list, list(df_object = data.frame(a = 1:5, b = 6:10)))

> print(my_list)
#' $string_vector
#' [1] "string"
#' 
#' $numeric_vector
#' [1] 1 2 3 4 5
#' 
#' $list_object
#' $list_object[[1]]
#' [1] "string"
#' 
#' $list_object[[2]]
#' [1] 1 2 3 4 5
#' 
#' 
#' $logical_vector
#' [1]  TRUE FALSE
#' 
#' $df_object
#'   a  b
#' 1 1  6
#' 2 2  7
#' 3 3  8
#' 4 4  9
#' 5 5 10

Removing list elements

Removing list elements can be achieved with the NULL keyword. Lets remove the data.frame object we added earlier as the 5th element of my_list.

> my_list[[5]] <- NULL

> print(my_list)
$string_vector
[1] "string"

$numeric_vector
[1] 1 2 3 4 5

$list_object
$list_object[[1]]
[1] "string"

$list_object[[2]]
[1] 1 2 3 4 5


$logical_vector
[1]  TRUE FALSE

We’re not limited to removing just elements from the end of the list. We can use the same technique to remove the 2nd element of my_list.

> my_list[[2]] <- NULL

> print(my_list)
$string_vector
[1] "string"

$list_object
$list_object[[1]]
[1] "string"

$list_object[[2]]
[1] 1 2 3 4 5


$logical_vector
[1]  TRUE FALSE

Iterating over lists

There are functions, such as lapply() that exist to iterate over lists which can help to provide optimised performance, though these will be covered elsewhere.

Iteration over a list with a loop works in much the same way as with other R objects.

> for (element in my_list) {
      print(element)
  }

#'[1] "string"
#'
#'[[1]]
#'[1] "string"
#'
#'[[2]]
#'[1] 1 2 3 4 5
#'
#'[1]  TRUE FALSE

Next steps

Lists are an important data structure within R and are particularly useful for solving some more advanced programming problems as well as for optimisation.

Their versatility makes them perfect for use in a variety of situations.

Try creating some lists consisting of different R objects and accessing the objects.