Merge and enframe several similar lists by element names, when one element includes a function

Viewed 51

I have several lists with the same structure (same list elements, different values), and I want to bind the lists using dplyr::bind_rows(). I encounter an error because one of the lists elements is a function.

Example

lst_a <- list(what_it_does = "adds 1", func = function (x) x + 1)
lst_b <- list(what_it_does = "subtracts 2", func = function(x) x - 2)
lst_c <- list(what_it_does = "takes a sqrt", func = function (x) sqrt(x))
lst_d <- list(what_it_does = "multiplies by 5", func = function (x) x * 5)

Desired output Combine the lists to one table (tibble)

# A tibble: 4 x 3
name   what_it_does    func  
<chr>  <chr>           <list>
lst_a  adds 1          <fn>  
lst_b  subtracts 2     <fn>  
lst_c  takes a sqrt    <fn>  
lst_d  multiplies by 5 <fn> 

My attempt
Initially I tried:

library(dplyr)

bind_rows(lst_a, lst_b, lst_c, lst_d)

Error: Input must be a vector, not a function.

Then I thought maybe I should be wrapping the func element in each list inside its own list(), which works:

lst_a_wrapped <- list(what_it_does = "adds 1", func = list(function (x) x + 1))
lst_b_wrapped <- list(what_it_does = "subtracts 2", func = list(function(x) x - 2))
lst_c_wrapped <- list(what_it_does = "takes a sqrt", func = list(function (x) sqrt(x)))
lst_d_wrapped <- list(what_it_does = "multiplies by 5", func = list(function (x) x * 5))

bind_rows(lst_a_wrapped, lst_b_wrapped, lst_c_wrapped, lst_d_wrapped)

## # A tibble: 4 x 2
##   what_it_does    func  
##   <chr>           <list>
## 1 adds 1          <fn>  
## 2 subtracts 2     <fn>  
## 3 takes a sqrt    <fn>  
## 4 multiplies by 5 <fn>  

My problem
Consider that I may have many lists to be tabulated, so manually wrapping each func element in each list with an inner list() is inelegant. Is there a way to go directly (meaning, programmatically) from {lst_a, lst_b, lst_c, lst_d} to the desired output? As far as I could find, bind_rows() doesn't have extra arguments to tweak the input, so the solution has to be other.

Bonus. Although it's not too important, I'd be happy to also learn whether there's a way to achieve a name column as in the desired output, derived from the lists object names, as part of the process.

2 Answers

You can add the list around the function in a loop like:

x <- mget(c("lst_a", "lst_b", "lst_c", "lst_d"))
y <- lapply(x, function(x) lapply(x, function(y) if(is.function(y)) list(y) else y))
dplyr::bind_rows(y)
## A tibble: 4 x 2
#  what_it_does    func  
#  <chr>           <list>
#1 adds 1          <fn>  
#2 subtracts 2     <fn>  
#3 takes a sqrt    <fn>  
#4 multiplies by 5 <fn>  

If you have many lists with common prefix such as "lst_", you may want to consider using ls function.

all_lsts <- mget(ls(pattern = "^lst_"))

all_lsts_fixed <- lapply(all_lsts, function(x) lapply(x, function(y) if(is.function(y)) list(y) else y))

dplyr::bind_rows(all_lsts_fixed)

# # A tibble: 4 x 2
# what_it_does    func  
# <chr>           <list>
#   1 adds 1          <fn>  
#   2 subtracts 2     <fn>  
#   3 takes a sqrt    <fn>  
#   4 multiplies by 5 <fn> 
Related