Introduction
Did you know that the magrittr
pipe, %>%
, can be used for more than just data.frame
s and tibble
s? 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?