Coercion function within another function not operating properly

Hi everyone,

I am working on developing a R package that needs to coerce vectors, factors, data.frame, tibbles, data.tables, and list into a matrix of character values. For the coercion, I have created a function called char_matrix and returns a matrix of character values, where the column names of the matrix should be the original object or the columns of the original object. You can how I programmed this below. When I test the function using a vector of values only, a matrix is returned with the appropriate column name. However, when I put char_matrix within another function like main_fun, which is listed below, the returned character matrix has a column name of the parameter name and not the R object name.

Would someone be able to assist in adjusting char_matrix, so the appropriate R object name is returned as a column name when char_matrix is used within another function?

I have provided some examples below demonstrating the issue I am having. Thank you for the help!

Erik

# Create functions ####

## char_matrix ####
char_matrix <- function(x = x) {
  # Helper: Null-coalescing operator
  `%||%` <- function(a, b) {
    if (!is.null(x = a)) a else b
  }

  # Capture variable name as string for fallback column name ####
  var_name <- deparse(expr = substitute(expr = x))
  var_name <- gsub(
    x = var_name,
    pattern = ".*\\$",
    replacement = "",
    perl = FALSE
  )

  # Handle factor or character vector ####
  if (is.factor(x = x)) {
    x <- as.character(x = x)
  }

  # Simple vector to single-column character matrix ####
  if (is.atomic(x = x) && is.vector(x = x)) {
    out <- matrix(data = as.character(x = x), ncol = 1)
    colnames(x = out) <- var_name
    return(out)
  }

  # List input: handle unequal-length elements with padding ####
  if (
    is.list(x = x) && !inherits(x = x, what = c("data.frame", "data.table"))
  ) {
    # Creating padded character list ####
    max_len <- max(sapply(X = x, FUN = length))
    padded <- lapply(
      X = x,
      FUN = function(xx) {
        col_char <- as.character(x = xx)
        length(x = col_char) <- max_len # pads with NA automatically
        return(col_char)
      }
    )

    # Creating character matrix ####
    out <- do.call(what = "cbind", args = padded)
    colnames(x = out) <- names(x = x) %||% paste0("V", seq_along(padded))
    return(out)
  }

  # data.frame, data.table, or tibble ####
  if (inherits(x = x, what = c("data.frame", "data.table"))) {
    out <- as.matrix(
      x = data.frame(lapply(x, as.character), stringsAsFactors = FALSE)
    )
    colnames(x = out) <- colnames(x = x)
    return(out)
  }

  # Matrix input ####
  if (is.matrix(x = x)) {
    out <- apply(X = x, MARGIN = c(1, 2), FUN = as.character)
    colnames(x = out) <- colnames(x = x)
    return(out)
  }

  # Throwing an error if coercion fails ####
  stop(
    paste0(
      "x must be a vector, matrix, data.frame, data.table, tibble, factor,",
      " or list."
    )
  )
}


## Main function ####
main_fun <- function(y) {
  if (!inherits(x = y, what = "matrix")) {
    tmp <- try(expr = y <- char_matrix(x = y), silent = TRUE)
    if (inherits(x = tmp, what = "try-error")) {
      stop(
        paste0(
          "y must be a vector, matrix, data.frame, data.table, tibble, factor,",
          " or list."
        )
      )
    }
  }
  return(y)
}

# Generate data ####
z <- sample(x = letters[1:3], size = 100, replace = TRUE)

# Show char_matrix working outside of main function ####
char_working <- char_matrix(x = z)
colnames(char_working)

# Show char_matrix not working within main function ####
char_not_working <- main_fun(y = z)
colnames(char_not_working)

I'd advise against using non-standard evaluation like this and recommend you to read https://adv-r.hadley.nz/index.html.

The reason why it's not working is because substitute will look for the unevaluated expression in the current environment by default (char_matrix() here). If you want to get an expression in a different environment, you'll have to pass the desired environment to the function yourself. Besides, you're looking for an expression called x but the parameter in main_fun() is called y.

It is interesting to also note the impact of permanently declaring the only argument in the function x=x. I think that your function may not recognise external arguments being used.