2

I wrote this small utility function to nicely format monetary values:

Code

#' @title Money Format
#'
#' @description
#' The \code{money_format} function takes a numeric vector and returns formatted currency with pound sign.
#'
#' @param x numeric vector to be converted to currency
#' @param include_pound_sign A logical, defulats to \code{TRUE}.
#' @param digits An integer. Number of digits to pass to format.
#'
#' @details
#' The \code{money_format} function can be also used to pass nice UK-style SI unit formats
#'   with \code{include_pound_sign = FALSE}.
#'
#' @return
#' A character vector with suffiexes and pund sign
#'
#' @import scales
#'
#' @export
#'
#' @examples
#' money_format(1e6)
money_format <- function(x, include_pound_sign = TRUE, digits = 3) {
    stopifnot(is.numeric(x))

    # As per: https://stackoverflow.com/a/28160474/1655567
    f <-
        function(x) {
            div <- findInterval(x, c(1, 1e3, 1e6, 1e9, 1e12))
            suffixes <- c("_", "K", "m", "bn", "T")

            paste0({
                if (include_pound_sign) {
                    intToUtf8(163)
                }
            }
            , (
                scales::unit_format(
                    unit = suffixes[div],
                    scale = switch(
                        suffixes[div],
                        "_" = 1,
                        K = 1e-3,
                        m = 1e-6,
                        bn = 1e-9,
                        "T" =  1e-12
                    ),
                    sep = "",
                    digits = digits
                )
            )(x))
        }

    vf <- Vectorize(FUN = f, "x")

    vf(x)
}

Examples

>> money_format(1e3)
[1] "£1K"
>> money_format(1e6)
[1] "£1m"

Problem

I would like to display 100 as £100. The thing is that I'm using switch to select suffix and I can't use "" as variable name there. So far I was able to force "_" as variable name, which gives:

>> money_format(10)
[1] "£10_"

but I don't want that, I want £10. The problem is that I can't have:

   scale = switch(
                  suffixes[div],
                  "" = 1,
                # ^ - this fails

Desired solution

I don't want to be rewriting this function, is there some trick I could use to print out blank space for "" = 1 in my switch statement?

Konrad
  • 17,740
  • 16
  • 106
  • 167
  • 2
    Use " " instead of "_"? Then you get "£10 ". Or use something like gsub(" _", "", x, fixed = TRUE) – Koot6133 Oct 05 '17 at 13:37
  • @Koot6133 Yeah, **`" "`** can do, I was hoping that there is some trick to get **`"£10"`** but maybe I'm overcomplicating things. – Konrad Oct 05 '17 at 13:40

2 Answers2

1

This will use a default value of 1 for switch and allow the suffixes to be a zero length string

money_format <- function(x, include_pound_sign = TRUE, digits = 3) {
  stopifnot(is.numeric(x))
  div <- findInterval(x, c(1, 1e3, 1e6, 1e9, 1e12))
  suffixes <- c("", "K", "m", "bn", "T")
  sapply(seq_along(div), function(y) {
    sprintf("%s%s",
            ifelse(include_pound_sign, intToUtf8(163), NULL),
            scales::unit_format(
              unit = suffixes[div][y],
              scale = switch(
                suffixes[div][y],
                K = 1e-3,
                m = 1e-6,
                bn = 1e-9,
                "T" =  1e-12,
                1),
              sep = "",
              digits = digits
            )(x[y]))
    })
  }

val <- c(1.235e3, 1e6, 1009, 10)
money_format(val)
[1] "£1.24K" "£1m"    "£1.01K" "£10"  
manotheshark
  • 4,297
  • 17
  • 30
0

Something like this:

money_format <- function(x, include_pound_sign = TRUE, digits = 3) {
  stopifnot(is.numeric(x))

  # As per: https://stackoverflow.com/a/28160474/1655567
  f <-
    function(x) {
      div <- findInterval(x, c(1, 1e3, 1e6, 1e9, 1e12))
      suffixes <- c("_", "K", "m", "bn", "T")

      scale = switch(
        suffixes[div],
        "_" = 1,
        K = 1e-3,
        m = 1e-6,
        bn = 1e-9,
        "T" =  1e-12
      )

      if(scale == 1){
        suffixes[1] = ""
      }

      paste0({
        if (include_pound_sign) {
          intToUtf8(163)
        }
      }
      , (
        scales::unit_format(
          unit = suffixes[div],
          scale = scale,
          sep = "",
          digits = digits
        )
      )(x))
    }

  vf <- Vectorize(FUN = f, "x")

  vf(x)
}

I basically did the switch before the paste0 and replaced "_" with "" after the switch since suffixes is no longer needed.

Result:

> money_format(10)
[1] "£10"
> money_format(100)
[1] "£100"
> money_format(1000)
[1] "£1K"
> money_format(10000)
[1] "£10K"
> money_format(100000)
[1] "£100K"
acylam
  • 18,231
  • 5
  • 36
  • 45
  • Yeah, why not. I think with this question I was looking for a fancy solution and unnecessarily overcomplicating the problem. – Konrad Oct 05 '17 at 13:52
  • @Konrad I agree it's kind of a workaround, but the benefit of this method is that you no longer have to worry about what `switch` can take as long as you change it in the `if` statement. – acylam Oct 05 '17 at 13:56