Manipulating Lists

Accessing List Slots

R provides several mechanisms for accessing the contents of a list, each serving a distinct purpose. The three main operators used for this task are the dollar sign ($), single brackets ([]), and double brackets ([[]]).

The $ operator accesses a single list slot by name and returns the contents of that list slot.

l1 <- list(name = "bob",
           alive = TRUE,
           age = 33,
           relationshipStatus = 42+3i)

l1$name
[1] "bob"
class(l1$name)
[1] "character"

The [[]] operator works like the $ operator but also accepts numeric indices.

l1[["age"]]
[1] 33
l1[[2]]
[1] TRUE
class(l1[[2]])
[1] "logical"

The [] operator is notably different: it extracts any number of list slots and returns the result as a list. Even if only one element is selected, the output remains a list. This behavior is useful when we want to retain the list structure for further processing.

l1["name"]
$name
[1] "bob"
l1[2]
$alive
[1] TRUE
class(l1[2])
[1] "list"
l1[c("name", "alive")]
$name
[1] "bob"

$alive
[1] TRUE
l1[1:2]
$name
[1] "bob"

$alive
[1] TRUE
class(l1[1:2])
[1] "list"

If we want to select multiple list elements, the [] operator is our only option. You cannot use the $ or [[]] operators to select multiple list slots.

l1[[1:2]]
Error in l1[[1:2]]: subscript out of bounds
l1$c("name", "alive")
Error: attempt to apply non-function

Modifying List Elements

Above, we used the three selection operators to extract list elements, but you shouldn’t think about those operators as “subsetting operators”. It’s better to think about the selection operators as designating some part of a list for further processing. In the above examples, the further processing was simply printing the selected elements to the R console, but we can also use the selection operators to modify the designated list elements.

# View the original list
l1
$name
[1] "bob"

$alive
[1] TRUE

$age
[1] 33

$relationshipStatus
[1] 42+3i
## Modify some list elements
l1$age <- 57
l1[[1]] <- "suzy"
l1[c(2, 4)] <- c("foo", "bar")

# View the modified list
l1
$name
[1] "suzy"

$alive
[1] "foo"

$age
[1] 57

$relationshipStatus
[1] "bar"

Lists in R do not require a fixed length or predefined structure. New elements can be added at any point, either by position or by name. This flexibility makes lists well suited for incremental construction.

# Create an empty list
(l2 <- list())
list()
# Add new list slots
l2$grass <- "green"
l2$logical <- FALSE
l2[[3]] <- 1:4

l2
$grass
[1] "green"

$logical
[1] FALSE

[[3]]
[1] 1 2 3 4

Importantly, the types of the objects you store in a list are maintained.

(x <- 1:5)
[1] 1 2 3 4 5
(y <- list())
list()
(z <- letters[1:10])
 [1] "a" "b" "c" "d" "e" "f" "g" "h" "i" "j"
(l3 <- list(a = x, b = y, c = z))
$a
[1] 1 2 3 4 5

$b
list()

$c
 [1] "a" "b" "c" "d" "e" "f" "g" "h" "i" "j"
class(x)
[1] "integer"
class(y)
[1] "list"
class(z)
[1] "character"
class(l3$a)
[1] "integer"
class(l3$b)
[1] "list"
class(l3$c)
[1] "character"

Consequently, objects stored in a list behave just as they would outside of the list. For example, if a function is stored as an element of a list, we can call it directly:

# Create a trivial function
addOne <- function(x) { x + 1 }

# Store our function object in the 'l3' list
l3$myFunction <- addOne

# Call both versions of our function
addOne(2)
[1] 3
l3$myFunction(2)
[1] 3
Practice

Use the list that you created in the last practice problem for this exercise.

Using a single command, test if your eye color OR your hair color is also your favorite color.

Recall the basic logical operators:

  • ==, !=: Equal, Not Equal
  • >, <, >=, <=: Greater Than, Less Than
  • &, |: Logical AND, Logical OR (not exclusive)
myInfo$eyes == myInfo$color | myInfo$hair == myInfo$color
[1] FALSE
Back to top