Does a multi-value purrr::pluck exist?
Asked Answered
R

6

20

Seems like a basic question and perhaps I'm just missing something obvious ... but is there any way to pluck a sublist (with purrr)? More specifically, here's an initial list:

l <- list(a = "foo", b = "bar", c = "baz")

And I want to return a new (sub-)list with only elements a and b. Normally, I'd just do 'base' R sub-listing:

l[c("a", "b")]

But this doesn't provide the nice .default handling of pluck. My understanding is that pluck 'replaces' [[, but is there a purrr equivalent for replacing [?

Revise answered 27/10, 2017 at 21:35 Comment(1)
I don't think a direct 'replacement' for [ exists, at least not in purrr. I guess a workaround would be map(set_names(c("a", "b", "z")), partial(pluck, l), .default = "Not found!") but that's not very neat!Holstein
K
9

Similar to jzadra's post, you could also do:

l %>% keep(names(.) %in% c("a","b"))
Keeleykeelhaul answered 22/11, 2019 at 1:49 Comment(2)
This is a great solution but FYI it does not (currently) work when using the native pipe operator, |> (sadface).Latishalatitude
This doesn't retain the order in the list of names unfortunately, which can be useful sometimes.Devisor
H
8

Here's my take on it. Not based on purrr thus slightly off topic, but compatible with the tidyverse's general interface principle of playing nicely with pipes.

pluck_multiple <- function(x, ...) {
  `[`(x, ...)
}

x <- list(a = 1, b = 2, c = list(x = TRUE, y = FALSE))

pluck_multiple(x, c("a", "b"))
#> $a
#> [1] 1
#> 
#> $b
#> [1] 2
pluck_multiple(x, 2:3)
#> $b
#> [1] 2
#> 
#> $c
#> $c$x
#> [1] TRUE
#> 
#> $c$y
#> [1] FALSE
Honoria answered 16/12, 2019 at 22:4 Comment(0)
R
2

I think that purrr::keep() does what you want.

l %>% keep(names(.) == "a" | names(.) == "b")

Rubidium answered 20/3, 2018 at 17:26 Comment(1)
Or keep(names(.) %in% c("a", "b"))Swanhilda
K
2

There's clearly a need for "muti-value pluck". It really comes down to extending flexibility of "pluck" to retrieving multiple values from the same level of list, as described in this github issue. For the example above, you can probably get away with:

> purrr::map(purrr::set_names(c("a", "b")), 
          ~purrr::pluck(l, .x, .default = NA_character_))
#> $a
#> [1] "foo"
#> 
#> $b
#> [1] "bar"
Karat answered 24/4, 2018 at 9:20 Comment(0)
S
1

Not purrr, but magrittr has extract() as an alias for [:

library(magrittr)

l %>% 
  extract(c("a", "b"))
# $a
# [1] "foo"
# 
# $b
# [1] "bar"

magrittr implements a number of other pipeable operator aliases, too.

Superimposed answered 28/3 at 16:40 Comment(0)
D
0

Riffing off Rappster, if you don't mind the ugliness, you could pipe to the functional form of the extraction operator as a one-off:

> list(a = 1, b = 2, c = list(x = TRUE, y = FALSE)) %>% `[`(c('a', 'b'))
$a
[1] 1

$b
[1] 2

> list(a = 1, b = 2, c = list(x = TRUE, y = FALSE)) %>% `[`(2:3)
$b
[1] 2

$c
$c$x
[1] TRUE

$c$y
[1] FALSE

This has the added benefit of keeping the order of your inputs vs the keep approach, or allowing repeated extractions:

> c(a = 1, b = 2, c = 3) %>% `[`(2:3)
b c 
2 3 
> c(a = 1, b = 2, c = 3) %>% `[`(3:2)
c b 
3 2 
> c(a = 1, b = 2, c = 3) %>% keep(names(.) %in% c('a', 'b'))
a b 
1 2 
> c(a = 1, b = 2, c = 3) %>% keep(names(.) %in% c('b', 'a'))
a b 
1 2 
> c(a = 1, b = 2, c = 3) %>% `[`(c('a', 'a'))
a a 
1 1 
> c(a = 1, b = 2, c = 3) %>% keep(names(.) %in% c('a', 'a'))
a 
1 
Devisor answered 26/1 at 13:47 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.