2
votes

With the use of the various map() functions from the purrr package, you can pass an integer or a character string to the function and extract the element from a sublist. These work as I'd expect them to:

listy <- list(A = list("silence", "cats", X = list(Aa = "dogs", "birds")),
              B = list("silence", "fish-head", Y = list(Bb = "fish-face", "squirrel")))

> str(listy)
List of 2
 $ A:List of 3
  ..$  : chr "silence"
  ..$  : chr "cats"
  ..$ X:List of 2
  .. ..$ Aa: chr "dogs"
  .. ..$   : chr "birds"
 $ B:List of 3
  ..$  : chr "silence"
  ..$  : chr "fish-head"
  ..$ Y:List of 2
  .. ..$ Bb: chr "fish-face"
  .. ..$   : chr "squirrel"

list1 <- listy %>% map(1) %>% str  # returns "silence" from A and B
list2 <- listy %>% map(2) %>% str  # returns "cats" and "fish-head"
list3 <- listy %>% map(c(3, 1)) %>% str # returns "dogs" and "fish-face" from the lists X and Y within the list.

My question is, how do I extract more than one element from this list? If I wanted "silence" from A and B, and "cats" and "fish-head" from A and B (in other words, elements 1 and 2 of both A and B), is that possible with map()? And if not, what's the best way to do this?

Here's what I would have thought would work:

list4 <- listy %>% map(1, 2) %>% str

Where 1 refers to the first element from each sub-list, and 2 to the second. But this returns the same as list1, above. Using c(1, 2) does not work, as it refers to a nested structure (i.e. [[1]][[2]]). I've looked through the documentation and a few examples found through Google, but no luck. Any ideas?

Update: I should explain that ideally I'd like to be able to select the elements by name, as in "silence". This doesn't seem to be working too well, however. (I have several large lists where the position of the elements I want changes)

2
To "select elements by name," you will probably have to name the list elements. Working with named lists is probably the way to go. Then you can replace the 1:2 in our answers with a vector of element names without any further changes.lmo

2 Answers

5
votes

Something like this?

library(purrr)
listy %>% map(., function(x) c(x[[1]], x[[2]]))

$A
[1] "silence" "cats"   

$B
[1] "silence"   "fish-head"

To get the output in the form of a data.frame,

listy %>% map_df(., function(x) c(x[[1]], x[[2]]))

# A tibble: 2 x 2
        A         B
    <chr>     <chr>
1 silence   silence
2    cats fish-head

Or, as suggested by @Richard Scriven,

map(listy, ~ unlist(.[1:2]))
1
votes

Here are two options in base R with lapply.

To retain the nested list structure, use

lapply(listy, "[", 1:2)
$A
$A[[1]]
[1] "silence"

$A[[2]]
[1] "cats"


$B
$B[[1]]
[1] "silence"

$B[[2]]
[1] "fish-head"

To return a single list of vectors use

lapply(listy, function(i) unlist(i[1:2]))
$A

"silence"    "cats" 

$B

  "silence" "fish-head" 

You can wrap the second method in data.frame to return a data.frame.

data.frame(lapply(listy, function(i) unlist(i[1:2])))
        A         B
1 silence   silence
2    cats fish-head