4 min read

Get and Set List Elements with magrittr

Introduction

Did you know that the magrittr pipe, %>%, can be used for more than just data.frames and tibbles? In this blog post, we look at how we can create get and set functions for list elements.

Getting List Elements

First, let’s create a simple list.

z1 <- list(a = pi, b = 2.718, c = 0.57721)
z1
# $a
# [1] 3.141593
# 
# $b
# [1] 2.718
# 
# $c
# [1] 0.57721

Let’s say we want to access an element of this list, typically we would use the [[ function to do so.

z1[[2]]
# [1] 2.718

But let’s say we need to access this list as part of a chain using magrittr’s pipe operator, %>%. How can we do that? Well we can pipe our list into a . which acts as a placeholder for the list, on which we can perform our subset.

library(magrittr)
z1 %>% .[[2]]
# [1] 2.718

Another solution is to call [[ using its syntactic form [[() using backticks (or quotes, see ?Quotes).

z1 %>% `[[`(2)
# [1] 2.718

Admittedly, these two solutions don’t look very nice. So what we can do instead is assign the [[ function to an object which will, in effect, be a callable wrapper function.

get <- .Primitive("[[") # Equivalent to get <- `[[`
get(z1, 2)
# [1] 2.718

Primitives are functions that are internally implemented by R and so .Primitive("[[") tells R to dispatch to the underlying C code, which will be able to correctly identify which [[ method to use on the list class (see ?.Primitive for more details).

Since our list is now the first argument of get(), we have a much “cleaner” looking way of accessing elements of a list with the magrittr pipe operator than [[. And so, let’s access the second element of our list using get() and the magrittr pipe.

z1 %>% get(2)
# [1] 2.718

We can also access the list using its names, too.

z1 %>% get("b")
# [1] 2.718

It even works with recursive indexing!

z2 <- list(a = list(b = 9, c = "hello"), d = 1:5)
z2
# $a
# $a$b
# [1] 9
# 
# $a$c
# [1] "hello"
# 
# 
# $d
# [1] 1 2 3 4 5
z2 %>% get(c("a", "c")) # equivalent to z %>% get(c(1, 2))
# [1] "hello"

Note, you may want to choose a better name than get to avoid clashes with the base::get() function.

Setting List Elements

Similarly we can create a set() function to assign values to elements of our list using .Primitive("[[<-"). Let’s add a fourth element to our list.

set <- .Primitive("[[<-")
z1 <- z1 %>% set("d", 4.6692)
z1
# $a
# [1] 3.141593
# 
# $b
# [1] 2.718
# 
# $c
# [1] 0.57721
# 
# $d
# [1] 4.6692

And now just as set() giveth, set() taketh away.

z1 <- z1 %>% set("d", NULL)
z1
# $a
# [1] 3.141593
# 
# $b
# [1] 2.718
# 
# $c
# [1] 0.57721

Of course as this is a list, we can set any kind of data.

z1 %>% set("data", data.frame(a = c(1, 2, 2, 4), b = c(2, 3, 7, 4)))
# $a
# [1] 3.141593
# 
# $b
# [1] 2.718
# 
# $c
# [1] 0.57721
# 
# $data
#   a b
# 1 1 2
# 2 2 3
# 3 2 7
# 4 4 4

Or even overwrite elements.

z1 %>% set("b", 4.6692)
# $a
# [1] 3.141593
# 
# $b
# [1] 4.6692
# 
# $c
# [1] 0.57721

Conclusion

This was just a short blog post to highlight the power of magrittr in combination with R primitives. We also saw how to rewrite and manipulate syntactic forms of internal R functions. What other interesting use cases have you found for the magrittr pipe?