Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature Request: Write a list of data.frames to .csv files #207

Open
billdenney opened this issue Jun 22, 2019 · 1 comment

Comments

Projects
None yet
2 participants
@billdenney
Copy link

commented Jun 22, 2019

I have a feature request to allow writing a list of data.frames as many .csv files.

The interface I'm thinking of could be:

export(x=list_of_data_frames, file="file%s.csv", ...)

And, because I needed it for a project today, I wrote the below code which would support the above (the code below worked for my needs today, but it is not fully tested):

#' Write a list of data.frames to multiple .csv files.
#'
#' @details The \code{path} may be one of three path specifications:
#' \itemize{
#'   \item{A character vector the same length as \code{x} in which case the
#'   filenames are specified as that character vector.}
#'   \item{A character scalar with a single "%%s" indicating where the name of
#'   \code{x} should be inserted.  (Note, \code{sprintf()} is not used to
#'   generate the output filename.)}
#'   \item{A character scalar where the name of \code{x} will be inserted before
#'   the file extension.}
#' }
#' 
#' Names of \code{x} must be unique, or \code{x} must not have a name.  If
#' \code{x} has no names, then integers with left-padded zeros are used.
#'
#' @param x A list of data.frames
#' @param path The filename specification (see the Details section)
#' @return The list of files created (invisibly)
#' @examples
#' \dontrun{
#' example_data <- list(A=data.frame(A=1), B=data.frame(B=1))
#' write_csv_list(example_data, file.path(tempdir(), "test-%s.csv"))
#' }
#' @export
write_csv_list <- function(x, path, ...) {
  if (!is.character(path)) {
    stop("`path` must be a character scalar or vector.")
  }
  if (is.data.frame(x)) {
    stop("`x` must be a list of data.frames, not a data.frame.")
  } else if (!is.list(x)) {
    stop("`x` must be a list of data.frames.")
  } else if (!all(sapply(X=x, FUN=is.data.frame))) {
    stop("`x` must be a list of data.frames, not a list including any other object classes.")
  }
  x_names <-
    if (is.null(names(x))) {
      gsub(
        pattern=" ",
        replacement="0",
        x=format(seq_along(x), justify="right"),
        fixed=TRUE
      )
    } else {
      names(x)
    }
  if (any(x_names %in% "")) {
    stop("All or none of the elements of `x` must be named, empty names are not allowed.")
  } else if (any(duplicated(x_names))) {
    stop("All names of `x` must be unique.")
  }
  find_percent_s <- strsplit(path[1], split="%s", fixed=TRUE)[[1]]
  paths <-
    if (length(path) == length(x)) {
      if (any(duplicated(path))) {
        stop("All values of `path` must all be unique.")
      }
      path
    } else if (length(path) != 1) {
      stop("`path` must either be a single character string or a vector the same length as `x`.")
    } else if (length(find_percent_s) > 2) {
      stop("`path` may only contain a single '%s'.")
    } else if (length(find_percent_s) == 2) {
      paste0(find_percent_s[1], x_names, find_percent_s[2])
    } else if (length(find_percent_s) == 1) {
      message(
        "When giving a single character string for `path`, it is recommended ",
        "to insert '%s' where the names of `x` should be inserted into the ",
        "path name.  The name of `x` will be inserted before the file ",
        "extension."
      )
      file_ext <- rio::get_ext(path)
      tmp_path <- gsub(pattern=paste0(".", file_ext, "$"), replacement="", x=path)
      paste0(tmp_path, x_names, ".", file_ext)
    }
  for (idx in seq_along(x)) {
    write_csv(x=x[[idx]], path=paths[[idx]], ...)
  }
  invisible(paths)
}

If interested, I'm happy to make it into a PR.

@leeper leeper added the enhancement label Jun 26, 2019

@leeper

This comment has been minimized.

Copy link
Owner

commented Jun 26, 2019

Thanks - give me some time to think about how this should work.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.