From 7185a8736fb85a7889ee5dab6e655b383f5bba0c Mon Sep 17 00:00:00 2001 From: Joseph Larmarange Date: Sat, 7 Jul 2018 18:06:02 +0200 Subject: [PATCH] `dollar_format()` rely on `number_format()` This fixes #79 --- R/formatter.r | 101 +++++++++++++++++++++++--------- man/dollar_format.Rd | 43 +++++++++----- tests/testthat/test-formatter.r | 9 +++ 3 files changed, 111 insertions(+), 42 deletions(-) diff --git a/R/formatter.r b/R/formatter.r index 62175519..9ba1e126 100644 --- a/R/formatter.r +++ b/R/formatter.r @@ -105,19 +105,27 @@ comma <- function(x, ...) { #' Currency formatter: round to nearest cent and display dollar sign. #' #' The returned function will format a vector of values as currency. -#' Values are rounded to the nearest cent, and cents are displayed if -#' any of the values has a non-zero cents and the largest value is less -#' than `largest_with_cents` which by default is 100000. +#' If `accuracy` is not specified, values are rounded to the nearest cent, +#' and cents are displayed if any of the values has a non-zero cents and +#' the largest value is less #' than `largest_with_cents` which by default +#' is 100,000. #' -#' @return a function with single parameter x, a numeric vector, that -#' returns a character vector -#' @param largest_with_cents the value that all values of `x` must -#' be less than in order for the cents to be displayed -#' @param prefix,suffix Symbols to display before and after amount. -#' @param big.mark Character used between every 3 digits. +#' @return A function with single parameter `x`, a numeric vector, that +#' returns a character vector. +#' @param accuracy Number to round to, `NULL` for automatic guess. +#' @param scale A scaling factor: `x` will be multiply by `scale` before +#' formating (useful to display the data on another scale, e.g. in k$). +#' @param prefix,suffix Symbols to display before and after value. +#' @param big.mark Character used between every 3 digits to separate thousands. +#' @param decimal.mark The character to be used to indicate the numeric +#' decimal point. +#' @param trim Logical, if `FALSE`, values are right-justified to a common +#' width (see [base::format()]). +#' @param largest_with_cents The value that all values of `x` must +#' be less than in order for the cents to be displayed. #' @param negative_parens Should negative values be shown with parentheses? -#' @param ... Arguments passed on to [dollar()]. -#' @param x a numeric vector to format +#' @param ... Other arguments passed on to [base::format()]. +#' @param x A numeric vector to format. #' @export #' @examples #' dollar_format()(c(-100, 0.23, 1.456565, 2e3)) @@ -134,9 +142,35 @@ comma <- function(x, ...) { #' #' finance <- dollar_format(negative_parens = TRUE) #' finance(c(-100, 100)) -dollar_format <- function(...) { - force_all(...) - function(x) dollar(x, ...) +dollar_format <- function(accuracy = NULL, scale = 1, prefix = "$", + suffix = "", big.mark = "," , decimal.mark = ".", + trim = TRUE, largest_with_cents = 100000, + negative_parens = FALSE, ...) { + force_all( + accuracy, + scale, + prefix, + suffix, + big.mark, + decimal.mark, + trim, + largest_with_cents, + negative_parens, + ... + ) + function(x) dollar( + x, + accuracy = accuracy, + scale = scale, + prefix = prefix, + suffix = suffix, + big.mark = big.mark, + decimal.mark = decimal.mark, + trim = trim, + largest_with_cents = largest_with_cents, + negative_parens, + ... + ) } needs_cents <- function(x, threshold) { @@ -153,16 +187,20 @@ needs_cents <- function(x, threshold) { #' @export #' @rdname dollar_format -dollar <- function(x, prefix = "$", suffix = "", - largest_with_cents = 100000, big.mark = ",", - negative_parens = FALSE) { +dollar <- function(x, accuracy = NULL, scale = 1, prefix = "$", + suffix = "", big.mark = "," , decimal.mark = ".", + trim = TRUE, largest_with_cents = 100000, + negative_parens = FALSE, ...) { if (length(x) == 0) return(character()) - x <- round_any(x, 0.01) - if (needs_cents(x, largest_with_cents)) { - nsmall <- 2L - } else { - x <- round_any(x, 1) - nsmall <- 0L + if (is.null(accuracy)) { + if (needs_cents(x * scale, largest_with_cents)) { + accuracy <- .01 + } else { + accuracy <- 1 + } + } + if (identical(big.mark, ",") & identical(decimal.mark, ",")) { + big.mark <- " " } negative <- !is.na(x) & x < 0 @@ -170,15 +208,22 @@ dollar <- function(x, prefix = "$", suffix = "", x <- abs(x) } - amount <- format(abs(x), - nsmall = nsmall, trim = TRUE, big.mark = big.mark, - scientific = FALSE, digits = 1L, + amount <- number( + x, + accuracy = accuracy, + scale = scale, + prefix = prefix, + suffix = suffix, + big.mark = big.mark, + decimal.mark = decimal.mark, + trim = trim, + ... ) if (negative_parens) { - paste0(ifelse(negative, "(", ""), prefix, amount, suffix, ifelse(negative, ")", "")) + paste0(ifelse(negative, "(", ""), amount, ifelse(negative, ")", "")) } else { - paste0(prefix, ifelse(negative, "-", ""), amount, suffix) + amount } } diff --git a/man/dollar_format.Rd b/man/dollar_format.Rd index 188ab896..f8497c26 100644 --- a/man/dollar_format.Rd +++ b/man/dollar_format.Rd @@ -5,34 +5,49 @@ \alias{dollar} \title{Currency formatter: round to nearest cent and display dollar sign.} \usage{ -dollar_format(...) +dollar_format(accuracy = NULL, scale = 1, prefix = "$", suffix = "", + big.mark = ",", decimal.mark = ".", trim = TRUE, + largest_with_cents = 1e+05, negative_parens = FALSE, ...) -dollar(x, prefix = "$", suffix = "", largest_with_cents = 1e+05, - big.mark = ",", negative_parens = FALSE) +dollar(x, accuracy = NULL, scale = 1, prefix = "$", suffix = "", + big.mark = ",", decimal.mark = ".", trim = TRUE, + largest_with_cents = 1e+05, negative_parens = FALSE, ...) } \arguments{ -\item{...}{Arguments passed on to \code{\link[=dollar]{dollar()}}.} +\item{accuracy}{Number to round to, \code{NULL} for automatic guess.} -\item{x}{a numeric vector to format} +\item{scale}{A scaling factor: \code{x} will be multiply by \code{scale} before +formating (useful to display the data on another scale, e.g. in k$).} -\item{prefix, suffix}{Symbols to display before and after amount.} +\item{prefix, suffix}{Symbols to display before and after value.} -\item{largest_with_cents}{the value that all values of \code{x} must -be less than in order for the cents to be displayed} +\item{big.mark}{Character used between every 3 digits to separate thousands.} -\item{big.mark}{Character used between every 3 digits.} +\item{decimal.mark}{The character to be used to indicate the numeric +decimal point.} + +\item{trim}{Logical, if \code{FALSE}, values are right-justified to a common +width (see \code{\link[base:format]{base::format()}}).} + +\item{largest_with_cents}{The value that all values of \code{x} must +be less than in order for the cents to be displayed.} \item{negative_parens}{Should negative values be shown with parentheses?} + +\item{...}{Other arguments passed on to \code{\link[base:format]{base::format()}}.} + +\item{x}{A numeric vector to format.} } \value{ -a function with single parameter x, a numeric vector, that -returns a character vector +A function with single parameter \code{x}, a numeric vector, that +returns a character vector. } \description{ The returned function will format a vector of values as currency. -Values are rounded to the nearest cent, and cents are displayed if -any of the values has a non-zero cents and the largest value is less -than \code{largest_with_cents} which by default is 100000. +If \code{accuracy} is not specified, values are rounded to the nearest cent, +and cents are displayed if any of the values has a non-zero cents and +the largest value is less #' than \code{largest_with_cents} which by default +is 100,000. } \examples{ dollar_format()(c(-100, 0.23, 1.456565, 2e3)) diff --git a/tests/testthat/test-formatter.r b/tests/testthat/test-formatter.r index 588c2d6f..ca1355f2 100644 --- a/tests/testthat/test-formatter.r +++ b/tests/testthat/test-formatter.r @@ -146,6 +146,15 @@ test_that("negative comes before prefix", { expect_equal(dollar(-1), "$-1") }) +test_that("prefix is inside parentheses", { + expect_equal(dollar(-1, negative_parens = TRUE), "($1)") +}) + test_that("missing values preserved", { expect_equal(dollar(NA_real_), "$NA") }) + +test_that("decimal.mark could be modified", { + expect_equal(dollar(123.45, decimal.mark = ","), "$123,45") +}) +