8

Consider this toy function that receives 3 arguments:

toy <- function(x, y, z){
    paste(x, y, z)
}

For each argument I have a number of values (not necessarily the same number for each argument) and I want to apply the toy function to the different combinations of those arguments.

So I thought, ok, let's use the multivariate version of the apply functions mapply.

mapply(FUN = toy, x = 1:2, y = c("#", "$"), z = c("a", "b"))

[1] "1 # a" "2 $ b"

But this is not quite what I wanted. Indeed, according to the help mapply "applies FUN to the first elements of each ... argument, the second elements, the third elements, and so on.". And what I want is to apply FUN to all the different combinations of all the arguments.

So, instead of

[1] "1 # a" "2 $ b"

The result I would like is rather:

[1] "1 # a" "1 # b" "1 $ a" "1 $ b" "2 # a" "2 # b" "2 $ a" "2 $ b"

So, my question is what is the clever way to do this?

Of course, I can prepare the combinations beforehand and arrange the arguments for mapply so they include -rowwise- all the combinations. But I just thought this may be a rather common task and there might be already a function, within the apply-family, that can do that.

elikesprogramming
  • 2,506
  • 2
  • 19
  • 37

1 Answers1

11

You could combine do.call with expand.grid to work with unlimited amount of input as follows

toy <- function(...) do.call(paste, expand.grid(...))

Then, you could do

x = 1:2 ; y = c("#", "$") ; z = c("a", "b")
toy(x, y, z)
# [1] "1 # a" "2 # a" "1 $ a" "2 $ a" "1 # b" "2 # b" "1 $ b" "2 $ b"

This will work for any input. You could try stuff like toy(x, y, z, y, y, y, z), for instance in order to validate.

David Arenburg
  • 91,361
  • 17
  • 137
  • 196
  • 4
    thanks `expand.grid` works to prepare the combinations. I just had to do it in two steps. First, `args <- expand.grid(x = 1:2, y = c("#", "$"), z = c("a", "b"))` then `mapply(FUN = toy, x = args$x, y = args$y, z = args$z)`, because doing it directly like this `mapply(FUN = toy, expand.grid(x = 1:2, y = c("#", "$"), z = c("a", "b")))` would not work (`mapply` would receive only one argument; a `data.frame`). I just thought there was some function that internally does the trick. Using `do.call` and `paste` it's for this particular toy function. My actual function is different . – elikesprogramming Mar 09 '16 at 12:37
  • Ok, though `do.call` is vectorized and much more efficient than `mapply`, but maybe it is not always can be expended to more general cases. – David Arenburg Mar 09 '16 at 12:39