Here's a solution that manipulates language rather than strings. You and others might also find op_literal() useful in the future.
Solution
Helper: op_literal()
This helper function op_literal() actually manipulates the R language itself to repetitively use a binary operator like + across many operands...even though a binary operator typically accepts only two operands. Calling op_literal(`+`, w, x, y, z) will actually generate this expression here: w + x + y + z.
# Helper function to arbitrarily repeat a binary operation (like '+').
op_literal <- function(op, ...) {
# Capture the operator as a symbol.
op_sym <- rlang::ensym(op)
# Count the operands.
n_dots <- rlang::dots_n(...)
# Recursive case: a binary operator cannot handle this many arguments.
if(n_dots > 2) {
# Split off the final operand.
dots <- rlang::exprs(...)
dots_last <- dots[[n_dots]]
dots <- dots[-n_dots]
# Perform recursion for the remaining operands.
op_left <- rlang::inject(op_literal(
op = !!op_sym,
... = !!!dots
))
# Assemble recursive results into the full operation.
substitute(op(op_left, dots_last))
}
# Base case: the binary operator can handle 2(-) arguments.
else {
substitute(op(...))
}
}
Note
Since op_literal() generates an expression, you still need to evaluate it if you want the result:
op_exp <- op_literal(`+`, 1, 2, 3, 4)
op_exp
#> 1 + 2 + 3 + 4
eval(op_exp)
#> [1] 10
Custom Function: print.f()
Next, this custom print.f() then leverages op_literal() to assemble the formula:
# Your custom 'print.f()' function.
print.f <- function(data, var1, ..., group) {
# Capture the core variables as symbols.
group_var <- rlang::ensym(group)
other_vars <- rlang::ensym(var1)
# Count the additional variables.
n_dots <- rlang::dots_n(...)
# Append those other variables if they exist.
if(n_dots > 0) {
other_vars <- rlang::inject(op_literal(op = `+`, !!other_vars, ...))
}
# Assemble the formula.
formula_exp <- rlang::inject(~ !!other_vars | !!group_var)
# Generate the table according to that formula.
table1::table1(
formula_exp,
data = data
)
}
Result
Given your dataset reproduced here
dataset <- data.frame(
ID = c(1, 1, 2, 2, 3, 3, 4, 4),
group = c("gp1", "gp2", "gp1", "gp2", "gp1", "gp2", "gp1", "gp2"),
col1 = c(0, 1, 1, 1, 0, 1, 1, 0),
col2 = c(0, 0, 1, 1, 1, 1, 0, 0),
col3 = c(1, 0, 1, 0, 1, 1, 1, 0)
)
your call to print.f()
print.f(dataset, col1, col2, col3, group = group)
should yield the following visualization:

Note
As it stands, you have defined the group parameter at the end of your function header. This means that if you try calling print.f() like so
print.f(data = dataset, var = col1, col2, col3, group)
then you will get an error: without the group = specification, that final variable gets lumped together with col2 and col3, all under the ... umbrella. This will generate a bad formula:
~ col1 + col2 + col3 + group |
To avoid the pain of having to type out group = every time, you can simply relocate it before the ..., like so:
print.f <- function(data, group, var1, ...) {
# ^^^^^
Once you've done so, the following call will work as you intended:
print.f(dataset, group, col1, col2, col3)