David R
David R

Reputation: 131

lapply and ifelse together not working as expected

I am writing a doc about R and I've come up with an example that I cannot understand. Let us say this:

mylist <- list(8, 1:10, c(4,7,25), matrix(1:30, ncol = 5), "my name is not Donald")
lapply(1:length(mylist), function(x) ifelse(is.numeric(mylist[[x]]), 
  mylist[[x]]*2, mylist[[x]]))

I would have expected the numeric elements in my list to be 2-folded and the character vector to be left a it is. Instead, the result for numeric elements in the list is just the first component of each element, as if the ifelse's TRUE condition (i.e. 1) had taken over the lapply's x index. Could anybody please tell me the logic behind this, and what should I have typed instead? Thanks a lot. Best, David

Upvotes: 1

Views: 224

Answers (2)

G. Grothendieck
G. Grothendieck

Reputation: 269421

1) if We want if, not ifelse here. Have also simplified the code a bit.

f <- function(x) if (is.numeric(x)) x*2 else x
lapply(mylist, f)

giving:

[[1]]
[1] 16

[[2]]
 [1]  2  4  6  8 10 12 14 16 18 20

[[3]]
[1]  8 14 50

[[4]]
     [,1] [,2] [,3] [,4] [,5]
[1,]    2   14   26   38   50
[2,]    4   16   28   40   52
[3,]    6   18   30   42   54
[4,]    8   20   32   44   56
[5,]   10   22   34   46   58
[6,]   12   24   36   48   60

[[5]]
[1] "my name is not Donald"

2) S3 Although this is probably a bit too complex to warrant in this case it could be useful if we had a larger set of classes and it is also shows that this could be done without any if using S3. To do that we define an S3 generic with numeric and default methods and then S3 will automatically dispatch the appropriate method.

dble <- function(x, ...) UseMethod("dble")
dble.numeric <- function(x, ...) 2 * x
dble.default <- function(x, ...) x

lapply(mylist, dble)

3) tryCatch Another way which does not use if is to double the argument and if it raises an error catch it. Again this is probably too complex to warrant in this case but it illustrates what could be done if the actual situation were more complex.

f2 <- function(x) tryCatch(2 * x, error = function(e) x)
lapply(mylist, f2)

4) rrapply The rrapply function in the rrapply package can specify a condition such that the function is only applied to the elements satisfying it.

library(rrapply)

rrapply(mylist, is.numeric, function(x) 2 * x)

5) modify_if The purrr package also has a function that will modify only elements that satisfy a condition.

library(purrr)

modify_if(mylist, is.numeric, `*`, 2)

Upvotes: 5

Duck
Duck

Reputation: 39585

The issue is that the condition is only creating one TRUE, instead try something like this:

#Code
lapply(1:length(mylist),
       function(x) ifelse(lapply(mylist[[x]],is.numeric),
                          mylist[[x]]*2, mylist[[x]]))

The issue is that the matrix structure will be lost, so the answer from @G.Grothendieck would be a better option.

Upvotes: 0

Related Questions