String interpolation is a common feature of many programming languages and is used to insert the value of variables directly into character strings. This is useful to compose dynamic messages in a natural way. For example "Hello {x}"
will be interpolated as âHello Johnâ if x = "John"
.
What if you want to apply some modifications to the variable before insertion? Like normalizing the case, trimming at 40 characters, or concatenating? In general, you canât. Well, actually you can but have to use the languageâs own native tools. And it can be surprising how performing a few basic operations can quickly amount to lot of typing.
The aim of the package stringmagic
is to simplify and empower string interpolation. With the string_magic
function, you can apply 50+ basic operations to interpolated variables, interpolations can be nested, there is advanced support for pluralization, and much more.
string_magic
behaves in a similar way to the well known function glue. Almost anything glue
can do, string_magic
can do. Use curly brackets to interpolate variables: i.e to insert their value directly in the string:
library(stringmagic)
x = "John" ; y = "Mary"
string_magic("Hi {x}! How's {y} doing?")
#> [1] "Hi John! How's Mary doing?"
You can either interpolate a variable that already exists, as above, or provide the variable as an argument:
string_magic("Hi {x}! How's {y} doing?", y = "Jane")
#> [1] "Hi John! How's Jane doing?"
So far, so good. Now letâs come to string_magic
âs originality: the ability to add operations to interpolations.
In string_magic
you can apply any arbitrary chain of operations to the interpolated variables. The syntax is as follows:
The operations
are a comma separated sequence of keywords, each keyword being bound to a specific function. Here is an example in which we apply two operations:
lovers = c("romeo", "juliet")
string_magic("Famous lovers: {title, enum ? lovers}.")
#> [1] "Famous lovers: Romeo and Juliet."
As shown, title
puts to title case and enum
creates an enumeration (i.e. turns the elements of a vector into a single character string). These two operations are applied sequentially from left to right to the variable lovers
. You can stack as many operations as you wish.
Here is a short list of common operations: enum
to enumerate; lower
, upper
and title
to change the case; unik
to create the vector of unique elements; n
, N
to format integers; split
to break the string with respect to a pattern; replace
to make pattern replacements; collapse
to concatenate the strings; first
to select the first elements; q
, Q
and bq
for quoting; align
to align the elements.
The question mark defines the separation between the operations and the expression. After the question mark, you can use any valid R expression. For example:
string_magic("The max of each variable is: {enum ? sapply(iris[, 1:4], max)}.")
#> [1] "The max of each variable is: 7.9, 4.4, 6.9 and 2.5."
Adding arguments to operations
Many operations use arguments. Arguments are passed using quoted text just before the operation. The syntax is:
Letâs take the example of splitting an email address and keeping the text before the domain:
email = "John@Doe.com"
string_magic("This message comes from {'@'split, first ? email}.")
#> [1] "This message comes from John."
In this example, split
breaks "John@Doe.com"
with respect to "@"
, then first
keeps only the first element. Almost all operations have default values. For split
, the default value is a single space. The operation first
also accepts arguments and its default is 1. Letâs change it to keep the first two values:
string_magic("The first two species are: {unik, '2'first, q, enum ? iris$Species}.")
#> [1] "The first two species are: 'setosa' and 'versicolor'."
Alternative ways to pass arguments
There are three ways to pass arguments to operations:
The first, canonical way, is using quotes like in the examples of the previous section.
The second way uses a space to separate the argument from the operation and hence forbids any space in the argument.
string_magic("The first five sepal lengths are: {5 first, enum ? iris$Sepal.Length}.")
#> [1] "The first five sepal lengths are: 5.1, 4.9, 4.7, 4.6 and 5."
The third way uses backticks. Passing arguments with backticks requests an evaluation of the argument, using the variables defined in the calling environment. Letâs redo the previous example with argument evaluation:
n = 5
string_magic("The first {N?n} sepal lengths are: {`n`first, enum ? iris$Sepal.Length}.")
#> [1] "The first five sepal lengths are: 5.1, 4.9, 4.7, 4.6 and 5."
Adding options to operations
Options are keywords working like flags (i.e. things that can be turned on) and change the behavior of the current operation. Many operations acccept options. Add options using a dot separated sequence of keywords attached to the operation:
We have seen the enum
operation in an earlier example, letâs add a couple of options to it.
fields = c("maths", "physics")
string_magic("This position requires a PhD in either: {enum.i.or ? fields}.")
#> [1] "This position requires a PhD in either: i) maths, or ii) physics."
As just seen, the option i
triggers an enumeration with roman lower case numbers, and option or
finishes the enumeration with an or
instead of an and
. The order of the options does not matter. Note that there is partial matching on the options, so that long option names can be shortened.
Letâs have another example where we wish to add upper cases but only for the first letter of each sentence. To do this, we use the option sentence
of the operation upper
:
oversight = "hey, you forgot. forgot what? forgot the capital letters!"
string_magic("{upper.sentence ? oversight}")
#> [1] "Hey, you forgot. Forgot what? Forgot the capital letters!"
Wrapping up. With string_magic
you can perform string interpolations à la glue
and also empower these interpolations by adding operations on-the-fly (see the operations reference). This can take us a long way already. We are presenting next how you can nest interpolations and follow with an introduction to pluralization.
In string_magic
, you have two types of interpolations: regular interpolations (the ones we have covered so far), and verbatim evaluation. The syntax of vertatim interpolations is as follows:
Anything after the exclamation mark is treated as verbatim: i.e. anything coming before the closing bracket is treated as a simple character string. Letâs give an example:
string_magic("Hello {upper ! world}.")
#> [1] "Hello WORLD."
In that case, since world
is placed after the excalamation mark (and not a question mark!), the character string "world"
is passed to the operation upper
, leading to the result.
The main interest of verbatim evaluations is nesting. Indeed, the function string_magic
is always applied to the verbatim character string before any operation is applied. This means that interpolations within the verbatim text will be resolved. For example, letâs create the sum from x1
to x3
:
string_magic("y = {' + 'collapse ! x{1:3}}")
#> [1] "y = x1 + x2 + x3"
Now letâs tackle a more challenging example: write a polynomial of order n
. It should be of the form a + bx + cx^2 + etc
.
n = 2
string_magic("poly({n}): {' + 'collapse ! {letters[1 + 0:n]}x^{0:n}}")
#> [1] "poly(2): ax^0 + bx^1 + cx^2"
Letâs break it down. The verbatim expression (the one following the exclamation mark), here "{letters[1 + 0:n]}x^{0:n}"
, is evaluated with string_magic
. string_magic("{letters[1 + 0:n]}x^{0:n}")
leads to the vector c("ax^0", "bx^1", "cx^2")
.
The operation ' + 'collapse
then concatenates that vector with ' + '
. This value is then appended to the previous string.
We can refine by adding a cleaning operation in which we replace "x^0"
and "^1"
by the empty string. Letâs do it:
n = 4
string_magic("poly({n}): {' + 'c, 'f/x^0, ^1'clean ! {letters[1 + 0:n]}x^{0:n}}")
#> [1] "poly(4): a + bx + cx^2 + dx^3 + ex^4"
To achieve our goal, we used the operation clean
which replaces comma separated regular expression patterns (of the form 'flags/pat1, pat2, etc'
) with the empty string. We also used the flag f
(standing for âfixedâ) to suppress the regular expression meaning of the patterns (because '^'
has a specific meaning in regexes) â see the vignette on flags for more information. Finally, you can notice that the operation collapse
has a single letter alias.
Pluralization is about the automatic writing of a message in singular or plural form, depending on context. This feature is especially important for developers who often need to write human readable messages to inform the user. string_magic
has an extensive support for pluralization, which we briefly introduce here.
The syntax for pluralization is as follows:
After the opening bracket, to trigger pluralization use an hashtag (#
) or a dollar sign ($
) followed by plural operations. There is two kinds of pluralization: either on the value of the expresison (the #
), either on its length (the $
). Here is an example of pluralization on length with three operations:
friends = c("Piglet", "Eeyore")
string_magic("My best friend{$s, are, enum ? friends}. Who am I?")
#> [1] "My best friends are Piglet and Eeyore. Who am I?"
friends = "Mercutio"
string_magic("My best friend{$s, are, enum ? friends}. Who am I?")
#> [1] "My best friend is Mercutio. Who am I?"
As you can see, the name of the operations is intuitive: s
to add an âsâ when plural, are
to write is
when singular and are
when plural (the operation is
is identical), and enum
to create an enumeration.
Note that in plural interpolations the expression over which to pluralize is optional. It is automaically deduced from the previous interpolations. Now letâs illustrate this behavior with a pluralization on value:
nFiles = 6
string_magic("Warning: {N?nFiles} file{#s, is} corrupted.")
#> [1] "Warning: six files are corrupted."
nFiles = 1
string_magic("Warning: {N?nFiles} file{#s, is} corrupted.")
#> [1] "Warning: one file is corrupted."
The first interpolation is a regular one. The second one, {#s, is}
, deduces the expression over which to pluralize from this previous interpolation.
This was only an introduction. To have more information on pluralization, please see the dedicated section.
Escaping and special casesWe end this introduction with a few special cases.
Escape bracketsThe opening and closing brakets, {}
, are special characters and cannot be used as regular text. To bypass their special meaning, you need to escape them with a double backslash.
You only need to escape the special delimiters which the algorithm is currently looking for. For example, if we escape the opening delimiter, we donât need to escape the closing one:
# Here opening delimiter is escaped: the closing delimiter has no special meaning
string_magic("open = \\{, close = }")
#> [1] "open = {, close = }"
You need to escape the closing delimiter only when the algorithm is looking for it. In the following example, we need to escape the closing curly bracket to treat it as a regular character:
string_magic("Here are closing brackets: {5 times.c ! \\}}")
#> [1] "Here are closing brackets: }}}}}"
Alternatively, use the argument .delim
to change the delimiters:
string_magic("Here I {interpolate} with .[this] ", .delim = ".[ ]", this = ".[]")
#> [1] "Here I {interpolate} with .[] "
When using delimiters composed of two or more characters, like in the previous example, to escape it you need to add a double backslash before the first character (i.e. right before the delimiter):
string_magic("Here I {interpolate} with \\.[] ", .delim = ".[ ]")
#> [1] "Here I {interpolate} with .[] "
# another example with {{ }} as delimiter
string_magic("I {{what}} with \\{{ }} ", .delim = "{{ }}", what = "interpolate")
#> [1] "I interpolate with {{ }} "
Another way is to pass a character string in an expression. Indeed, since {expr}
evaluates expr
, if expr
is a character string, it will stay untouched.
string_magic("I {'can {write} {{what}} I want'}")
#> [1] "I can {write} {{what}} I want"
Other escapes
In the few operations expecting a semi-colon (if-else and certain operations in pluralization), it can also be escaped with a double backslash.
# if-else statement with semi-colon
is_c = TRUE
string_magic("{&is_c ; int i = 1\\; ; i = 1}")
#> [1] "int i = 1;"
is_c = FALSE
string_magic("{&is_c ; int i = 1\\; ; i = 1}")
#> [1] "i = 1"
How to use a regular !
in interpolations?
In interpolations, the exclamation mark (!
) signals a verbatim expression. But what if you use it to mean the logical operation not in an operation-free interpolation? In that case, you need a hack: use a question mark (?
) first to indicate to the algorithm that you want to evaluate the expression.
Ex.4: using the ?
hack to use !
as a logical operation.
string_magic("{!TRUE} is {?!TRUE}")
#> [1] "TRUE is FALSE"
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4