Design & Download
Your JS Library
in Seconds!!!

Snippet: sub() - String Placeholder Substitution

Written by Christopher West (cwest) on October 29, 2015.
Easily do string substitution in JavaScript with YourJS.sub(). Use similar syntax to that provided in ES6 with template strings. Handle pluralization of variables easily.
var sub;
(function(RGX) {
  sub = function(str, opt_subs) {
    opt_subs = opt_subs != undefined
      ? /^(Array|Object)$/.test(typeOf(opt_subs))
        ? opt_subs
        : slice(arguments, 1)
      : __global;
    var counter = 0;
    return str.replace(
      RGX,
      function(m, key, conditional, keyPlural, staticPlural, keySingular, staticSingular, mNone, keyNone, staticNone) {
        var args = slice(arguments); // To preserve the arguments passed to this function for any callback subs.
        keyPlural = keyPlural === '' ? counter++ : keyPlural;
        keySingular = keySingular === '' ? counter++ : keySingular;
        keyNone = keyNone === '' ? counter++ : keyNone;
        return has(opt_subs, key = key || counter++)
          ? (key = typeOf(key = opt_subs[key], 'Function') ? key.apply(str, args) : key, conditional)
            ? key - 1 != 0
              ? key || !mNone
                ? (keyPlural && has(opt_subs, keyPlural)
                  ? typeOf(keyPlural = opt_subs[keyPlural], 'Function')
                    ? keyPlural.apply(str, args)
                    : keyPlural
                  : staticPlural)
                : (keyNone && has(opt_subs, keyNone)
                  ? typeOf(keyNone = opt_subs[keyNone], 'Function')
                    ? keyNone.apply(str, args)
                    : keyNone
                  : staticNone)
              : (keySingular && has(opt_subs, keySingular)
                ? typeOf(keySingular = opt_subs[keySingular], 'Function')
                  ? keySingular.apply(str, args)
                  : keySingular
                : staticSingular)
            : key
          : m;
      }
    );
  };
})(/\{([\$\w]*)(\?(?:\{([\$\w]*)\}|([\s\S]*?))\:(?:\{([\$\w]*)\}|([\s\S]*?))(\:(?:\{([\$\w]*)\}|([\s\S]*?)))?)?\}/g);

Thanks to the awesomeness that is ES6 we can now easily sub strings into other strings:

`string text ${expression} string text`

Still it would be nice to have code that works similarly in plain JS now! The sub() function does just that with a few minor differences. First of all, in order to indicate a placeholder you must wrap the variable name in curly brackets (without prefixing it with a dollar sign):

// Print:  "Hello, my name is Chris West."
fname = "Chris";
lname = "West";
console.log(YourJS.sub("Hello, my name is {fname} {lname}."));

Next, instead of just using the global namespace (which is not recommended anyway), we can specify a context object:

// Print:  "Hello, my name is Chris West."
console.log(YourJS.sub("Hello, my name is {fname} {lname}.", {
  fname: 'Chris',
  lname: 'West'
}));

We can also leverage this function to singularize or pluralize or even "non"ize words.

/* Print:
 * I now have no apples.
 * I now have one apple.
 * I now have 2 apples.
 */
for (apples = 0; apples <= 2; apples++) {
  console.log(YourJS.sub("I now have {apples?{apples}:one:no} apple{apples?s:}."));
}

Again, the recommended way is to avoid polluting the global namespace so we could do this instead (assuming the code is called within a different namespace):

/* Print:
 * I now have no apples.
 * I now have one apple.
 * I now have 2 apples.
 */
for (var ctx = {apples: 0}; ctx.apples <= 2; ctx.apples++) {
  console.log(YourJS.sub("I now have {apples?{apples}:one:no} apple{apples?s:}.", ctx));
}

I personally feel that this function can be a great addition to your customized JS library so have fun using it!

sub(...) API Documentation

Description

Substitute values into strings where the corresponding placeholders have been entered.

Parameters

  1. str {string}:
    The string containing placeholders to be filled in and returned. A placeholder must correspond to a value in opt_subs and its name must be surrounded in curly braces (eg. "Hello {name}!" contains the name placeholder). If a placeholder refers to a number, a ternary operator can be used (eg. "You have {apples} apple{apples?s:}"). What appears between the ? and the : will replace the placeholder if the variable before the ? is not 1. What appears after the : will replace the placeholder if the variable before the ? is 1. A 4-ary (AKA quaterary) operator can also be used if a placeholder refers to a number (eg. "You have {apples?{apples}:one:no} apple{apples?s:}"). When using a 4-ary operator, whatever appears after the second : will replace the placeholder if the variable before the ? is 0.
  2. opt_subs {Array|Object}:
    Optional. Defaults to the global object. Contains the values to be used to fill in the placeholders in str.

Returns

Returns str with all of the valid placeholders filled in with their substitutions as found in opt_subs.