Bug in reduce from purrr in R?

111 Views Asked by At

In order to produce a markdown document with some strucure like

"Name and ID: %s Authors: %s Date of revision: %s"? 

and looped over

I tried

reduce(as.list(letters[1:3]),sprintf,fmt="%s %s %s")

which doesn't work, it throws the error

Error in fn(out, elt, ...) : too few arguments

I expected

[1] "a b c"

since

reduce(as.list(letters[1:2]),sprintf,fmt="%s %s")

works and outputs

[1] "a b"
> sessionInfo()
> R version 4.3.2 (2023-10-31 ucrt)
> Platform: x86_64-w64-mingw32/x64 (64-bit)
> Running under: Windows 10 x64 (build 19045)

Matrix products: default

locale:
\[1\] LC_COLLATE=English_United States.utf8  LC_CTYPE=English_United States.utf8    LC_MONETARY=English_United States.utf8
\[4\] LC_NUMERIC=C                           LC_TIME=English_United States.utf8

time zone: Europe/Paris
tzcode source: internal

attached base packages:
\[1\] stats     graphics  grDevices utils     datasets  methods   base

other attached packages:
\[1\] purrr_1.0.2

loaded via a namespace (and not attached):
\[1\] compiler_4.3.2    magrittr_2.0.3    cli_3.6.2         tools_4.3.2       rstudioapi_0.15.0 vctrs_0.6.5       lifecycle_1.0.4   rlang_1.1.3

> 
4

There are 4 best solutions below

0
stefan On BEST ANSWER

If reduce is needed you could achieve your desired result by using fmt="%s %s":

library(purrr)

reduce(
  as.list(letters[1:3]),
  sprintf,
  fmt = "%s %s"
)
#> [1] "a b c"

From the docs (?purrr::reduce) the function .f should be

... a 2-argument function. The function will be passed the accumulated value as the first argument and the "next" value as the second argument.

Hence, for your example reduce will pass two arguments to sprintf, the first is the accumulated value from the previous step and the second argument is the next value from your list of letters. But as you specified a format string with 3 arguments you get an error.

3
pbraeutigm On

If you type in ??reduce you will see that will purr:

"reduce() is an operation that combines the elements of a vector into a single value. The combination is driven by .f, a binary function that takes two values and returns a single value:"

So reduce can't work because it expects 2 values where you used 3. You could try another approach:

paste(letters[1:3], collapse = " ")
0
ThomasIsCoding On

I don't think you used reduce in a right way, but you can try the solutions for example as below

> x <- as.list(letters[1:3])

> reduce(x, paste)
[1] "a b c"

> do.call(paste, x)
[1] "a b c"
1
Onyambu On

Note that the function passed to reduce must take in two inputs. To do what you want using reduce, you could have:

reduce(letters[1:4], ~sprintf("%s %s", .x, .y))
[1] "a b c d"

think of it as:

 sprintf('%s %s', sprintf('%s %s', 'a', 'b'), 'c')

Please read more on reduce. Try writing the code using for-loop and see whether you understand the logic. That will simplify the task.


Perharps your goal could be achieved using a simpler version:

 exec(sprintf, '%s %s %s', !!!letters[1:3])