String Formatting in JavaScript

I am a relative newcomer to python, and have been blown away by the flexibility of some operations in Python. The string-formatting operator, %, is really wonderful and flexible. This is my attempt at implementing something similar in JavaScript.

Obviously, you can’t create a new operator in JavaScript, and in addition you can’t use % as a JavaScript identifier. So I went for the next-best thing:

String.prototype.format = function string_format(d) {
  // there are two modes of operation... unnamed indices are read in order;
  // named indices using %(name)s. The two styles cannot be mixed.
  // Unnamed indices can be passed as either a single argument to this function,
  // multiple arguments to this function, or as a single array argument
  let curindex = 0;

  if (arguments.length > 1)
    d = arguments;
  
  function r(s, key, type) {
    let v;
    if (key == "") {
      if (curindex == -1)
        throw Error("Cannot mix named and positional indices in string formatting.");

      if (curindex == 0 && (!(d instanceof Object) || !(0 in d)))
        v = d;
      else if (!(curindex in d))
        throw Error("Insufficient number of items in format, requesting item %i".format(curindex));
      else
        v = d[curindex];

      ++curindex;
    }
    else {
      key = key.slice(1, -1);
      if (curindex > 0)
        throw Error("Cannot mix named and positional indices in string formatting.");
      curindex = -1;
      
      if (!(key in d))
        throw Error("Key '%s' not present during string substitution.".format(key));
      v = d[key];
    }
    switch (type) {
    case "s":
      return v.toString();
    case "r":
      return v.toSource();
    case "i":
      return parseInt(v);
    case "f":
      return Number(v);
    case "%":
      return "%";
    default:
      throw Error("Unexpected format character '%s'.".format(type));
    }
  }
  return this.replace(/%(\([^)]+\))?(.)/g, r);
};
String.prototype.ø = String.prototype.format;

If you are at all familiar with the python string-formatting operator, this should be very similar:

"%s %s".ø("angry", "monkeys"); == "angry monkeys";
"%(key)s: %(value)s".ø({key: 'bananas', value: 'tasty'}) == "bananas: tasty";
"%i - %i".ø([1, 3]) == "1 - 3";
"%r".ø(['α', 'ω'] == '["α", "ω"]';

I know that there are many sprintf-like libraries out there for JavaScript: I just happen to like mine best. Caution: this code requires JavaScript 1.7… if you replace some “let”s with “var”s it may work in older browsers, but I haven’t tested it.

Being able to pass a function as a replacement in String.replace is a very powerful feature!

Atom Feed for Comments 7 Responses to “String Formatting in JavaScript”

  1. Anonymous Says:

    Python’s % should be considered obsolete, with format() being the recommended replacement in Python 3.
    I would look at duplicating format() instead of %.

  2. Michael K. Says:

    You named your anonymous function: “String.prototype.format = function string_format(d) {” Is that just a bug or intended? Calling string_format() doesn’t seem to work.

    Anyway! Really nice stuff, I think I’ll keep this one around. The syntax is really elegant. Man, I should really learn Python.

  3. Dao Says:

    Why do you use “let” anyway? Does it make any difference in this case?

  4. Benjamin Smedberg Says:

    Anonymous: the proposed .format() replacement uses keyword args, which are not supported by JavaScript. I’ll stick with this version.

    Michael: I name my anonymous functions so that backtraces and debuggers see them with a name. I wouldn’t expect you to call it directly.

    Dao: I use let because I wrote this code for a dehydra analysis in which we don’t use var scope at all, for consistency and readability.

  5. h3 Says:

    Not so long ago I’ve implemented Python3K’s string formating for jQuery, it was quite fun I must admit.

    > Anonymous: the proposed .format() replacement uses keyword args, which are not supported by JavaScript. I’ll stick with this version.

    It’s easy to work around, just use annotated objects as parameter..

    Anyway, it shouldn’t be considered as an equivalent to Python’s string formating, I’ve implemented it the best I could but JavaScript has its limitation.

    The code:
    http://code.google.com/p/jquery-utils/source/browse/trunk/src/jquery.strings.js

    Doc:
    http://code.google.com/p/jquery-utils/wiki/StringFormat

    Blog post:
    http://haineault.com/blog/58/

    Cheers

  6. Carlo Cabanilla Says:

    Hey, I like your library. There’s a tiny bug that makes it incompatible with Safari tho. Here’s a patch for the fix:

    Index: string_formatting.js
    ===================================================================
    — string_formatting.js (revision 28)
    +++ string_formatting.js (working copy)
    @@ -10,7 +10,7 @@

    function r(s, key, type) {
    var v;
    - if (key == “”) {
    + if (!key || key == “”) {
    if (curindex == -1)
    throw Error(“Cannot mix named and positional indices in string formatting.”);

  7. Bryan Says:

    Brilliant. Very creative making a new operator. This is the type of ingenuity that uses JavaScript in a brilliant and useful way, rather than using it as lipstick on a pig (a common practice in web design anymore)

Leave a Reply