Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Purrr with [[<-

Tags:

r

purrr

Can someone explain to me why this gives an error using purrr:

map(list(list(a=1, b=2, c=3), list(a=1, b=2, c=3)), `[[<-`, "b", 1)

but the same call with lapply seems to work.

I was under the impression that [[<- was just a function call, so was expecting both purrr and lapply to do the same thing here.

like image 366
Mark Avatar asked Nov 21 '25 05:11

Mark


1 Answers

The issue lies in purrr::as_mapper(), which map() calls under the hood. Consider the difference:

x <- list( a=1, b=2, c=3 )

`[[<-`( x, "b", 1 )                    # This is what lapply() calls
                                       # x is unchanged, returns modified list


purrr::as_mapper(`[[<-`)( x, "b", 1 )  # This is what map() calls
                                       # x is modified in-place, returns value 1

The second function call behaves as if you typed the following assignment expression:

(x[["b"]] <- 1)          
# [1] 1

which must be causing issues when the function is passed down to map() internals. Interestingly, wrapping the function with a ~ lambda works, but returns the "wrong" result:

y <- list(list(a=1, b=2, c=3), list(a=1, b=2, c=3))

purrr::map( y, ~purrr::as_mapper(`[[<-`)(.x, "b", 1) )
# [[1]]
# [1] 1

# [[2]]
# [1] 1

purrr::map( y, purrr::as_mapper(`[[<-`), "b", 1 )
# Error in list(a = 1, b = 2, c = 3)[["b"]] <- 1 : 
#   target of assignment expands to non-language object

The proper purrr equivalents to your lapply() example would instead look something like this:

r1 <- lapply(y, `[[<-`, "b", 1)
r2 <- purrr::map(y, purrr::modify_at, "b", ~1)
r3 <- purrr::map(y, ~`[[<-`(.x, "b", 1))

identical( r1, r2 )               # TRUE
identical( r1, r3 )               # TRUE

In the above, modify_at() returns the modified list, just like [[<-(...) does, and the tilde ~ is needed because modify_at() expects a function. The alternative is to wrap `[[<-` into a ~ lambda.

like image 143
Artem Sokolov Avatar answered Nov 23 '25 19:11

Artem Sokolov