Zero-padding with glue

432 Views Asked by At

I want to use glue to accomplish sprintf('%02d', x) but I'm not sure it's possible.

vignette('transformers') suggests a sprintf-alike transformer sprintf_transformer and "front end" wrapper glue_fmt like this:

sprintf_transformer <- function(text, envir) {
  m <- regexpr(":.+$", text)
  if (m != -1) {
    format <- substring(regmatches(text, m), 2)
    regmatches(text, m) <- ""
    res <- eval(parse(text = text, keep.source = FALSE), envir)
    do.call(sprintf, list(glue("%{format}f"), res))
  } else {
    eval(parse(text = text, keep.source = FALSE), envir)
  }
}

glue_fmt <- function(..., .envir = parent.frame()) {
  glue(..., .transformer = sprintf_transformer, .envir = .envir)
}

Then gives a simple example:

glue_fmt("π = {pi:.2}")
#> π = 3.14

This seems to rely on lazy evaluation to split the call pi:.2 (which is valid R syntax) into a call to sprintf: sprintf('%.2f', pi).

However it would appear to me this isn't possible for the case of 0-padding, since the R parser will eliminate any leading 0:

pid = as.integer(pi)
glue_fmt('{pid:02}')
# 3.000000
# vs desired
sprintf('%02d', pid)
# [1] "03"

Looking at sprintf_transformer, f is hard-coded, because 02d and .2f are not valid R syntax in and of themselves. I guess we could make glue_fmtd and glue_fmtf or add an argument to supply 'd' or 'f', but this is already straining the convenience factor of glue_fmt compared to plain sprintf.

And this would still not get over the fundamental constraint of the R parser -- as soon as 02 is treated as R code, the parser will drop the leading 0.

So, I'm stuck -- is it possible to use glue to do a zero-padded format in a way that's not overly convoluted?

1

There are 1 best solutions below

0
On BEST ANSWER

I don't think you need valid R syntax in the transfomer. The string is processed as a string. You don't need to hardcore the f or d. For example if you have

sprintf_transformer <- function(text, envir) {
  m <- regexpr(":.+$", text)
  if (m != -1) {
    format <- substring(regmatches(text, m), 2)
    regmatches(text, m) <- ""
    res <- eval(parse(text = text, keep.source = FALSE), envir)
    do.call(sprintf, list(glue("%{format}"), res))
  } else {
    eval(parse(text = text, keep.source = FALSE), envir)
  }
}

Then you can do

glue_fmt('{pid:02d}')
# 03
glue_fmt('{pid:0.02f}')
# 3.00