Categories
javascript printf string.format

JavaScript equivalent to printf/String.Format

2300

I’m looking for a good JavaScript equivalent of the C/PHP printf() or for C#/Java programmers, String.Format() (IFormatProvider for .NET).

My basic requirement is a thousand separator format for numbers for now, but something that handles lots of combinations (including dates) would be good.

I realize Microsoft’s Ajax library provides a version of String.Format(), but we don’t want the entire overhead of that framework.

6

  • 4

    Aside all the great answers below, you may want to take a look at this one: stackoverflow.com/a/2648463/1712065 which IMO, is the most efficient solution to this problem.

    – Annie

    Apr 24, 2015 at 2:54

  • 3

    I wrote a cheap one that uses C-like printf syntax.

    Jan 28, 2016 at 19:38

  • var search = [$scope.dog, “1”]; var url = vsprintf(“earth/Services/dogSearch.svc/FindMe/%s/%s“, search); ***For node, you can get your module by “npm install sprintf-js”

    Apr 29, 2016 at 19:25


  • I have also written a simple function to achieve this; stackoverflow.com/a/54345052/5927126

    Jan 24, 2019 at 11:10

  • 2

    Most of the answers here are disappointing. Both printf and String.Format are way more than just simple templating, and the question specifically mentions thousand separators, which none of the simple templating solutions handle.

    – blm

    Jan 23, 2021 at 23:01

1510

Current JavaScript

From ES6 on you could use template strings:

let soMany = 10;
console.log(`This is ${soMany} times easier!`);
// "This is 10 times easier!

See Kim’s answer below for details.


Older answer

Try sprintf() for JavaScript.


If you really want to do a simple format method on your own, don’t do the replacements successively but do them simultaneously.

Because most of the other proposals that are mentioned fail when a replace string of previous replacement does also contain a format sequence like this:

"{0}{1}".format("{1}", "{0}")

Normally you would expect the output to be {1}{0} but the actual output is {1}{1}. So do a simultaneously replacement instead like in fearphage’s suggestion.

13

  • 26

    If only some simple number-to-string conversion is desired, num.toFixed() method might be enough!

    Dec 17, 2012 at 23:29

  • 2

    @MaksymilianMajer that seems to be something massively different.

    Apr 14, 2014 at 21:51

  • @EvanCarroll you are right. At the time I wrote the comment the repository of sprintf() for JavaScript was not available. underscore.string has more features aside from sprintf which is based on sprintf() for JavaScript implementation. Other than that the library is an entirely different project.

    Apr 15, 2014 at 7:41

  • @MaksymilianMajer right, just saying this answer is dead, and the link has decayed. It needs to be totally purged.

    Apr 15, 2014 at 7:43

  • 4

    This shouldn’t be accepted answer anymore. As of ES6 this is built into the javascript language (both in browsers and NodeJS). See @Kim ‘s answer below.

    Jul 2, 2018 at 19:17

1482

Building on the previously suggested solutions:

// First, checks if it isn't implemented yet.
if (!String.prototype.format) {
  String.prototype.format = function() {
    var args = arguments;
    return this.replace(/{(\d+)}/g, function(match, number) { 
      return typeof args[number] != 'undefined'
        ? args[number]
        : match
      ;
    });
  };
}

"{0} is dead, but {1} is alive! {0} {2}".format("ASP", "ASP.NET")

outputs

ASP is dead, but ASP.NET is alive! ASP {2}


If you prefer not to modify String‘s prototype:

if (!String.format) {
  String.format = function(format) {
    var args = Array.prototype.slice.call(arguments, 1);
    return format.replace(/{(\d+)}/g, function(match, number) { 
      return typeof args[number] != 'undefined'
        ? args[number] 
        : match
      ;
    });
  };
}

Gives you the much more familiar:

String.format('{0} is dead, but {1} is alive! {0} {2}', 'ASP', 'ASP.NET');

with the same result:

ASP is dead, but ASP.NET is alive! ASP {2}

22

  • 14

    the || trick doesn’t work if args[number] is 0. Should do an explicit if() to see if (args[number] === undefined).

    – fserb

    Feb 19, 2011 at 14:16


  • 4

    in the else statement of the shorthand if, why not just do “match” instead of “‘{‘ + number + ‘}'”. match should equal that string.

    – mikeycgto

    Jul 10, 2011 at 4:40

  • 4

    If you have multiple strings appended to each other (with the +-operator), be sure to put the complete String in parentheses: ("asd {0}"+"fas {1}").format("first", "second"); Otherwise, the function will only be applied to the last string that was appended.

    Oct 22, 2012 at 17:59


  • 3

    That slightly and subtly changes the outcome. Imagine 'foo {0}'.format(fnWithNoReturnValue()). It would currently return foo {0}. With your changes, it would return foo undefined.

    – fearphage

    Feb 16, 2013 at 14:51

  • 4

    I think this is better than sprintf() for JS because it does basically the same thing and it is very small.

    Sep 8, 2013 at 18:26

608

It’s funny because Stack Overflow actually has their own formatting function for the String prototype called formatUnicorn. Try it! Go into the console and type something like:

"Hello, {name}, are you feeling {adjective}?".formatUnicorn({name:"Gabriel", adjective: "OK"});

Firebug

You get this output:

Hello, Gabriel, are you feeling OK?

You can use objects, arrays, and strings as arguments! I got its code and reworked it to produce a new version of String.prototype.format:

String.prototype.formatUnicorn = String.prototype.formatUnicorn ||
function () {
    "use strict";
    var str = this.toString();
    if (arguments.length) {
        var t = typeof arguments[0];
        var key;
        var args = ("string" === t || "number" === t) ?
            Array.prototype.slice.call(arguments)
            : arguments[0];

        for (key in args) {
            str = str.replace(new RegExp("\\{" + key + "\\}", "gi"), args[key]);
        }
    }

    return str;
};

Note the clever Array.prototype.slice.call(arguments) call — that means if you throw in arguments that are strings or numbers, not a single JSON-style object, you get C#’s String.Format behavior almost exactly.

"a{0}bcd{1}ef".formatUnicorn("FOO", "BAR"); // yields "aFOObcdBARef"

That’s because Array‘s slice will force whatever’s in arguments into an Array, whether it was originally or not, and the key will be the index (0, 1, 2…) of each array element coerced into a string (eg, “0”, so "\\{0\\}" for your first regexp pattern).

Neat.

21

  • 510

    It’s pretty cool to answer a question on stackoverflow with code from stackoverflow, +1

    Jan 18, 2014 at 0:02

  • 6

    @JamesManning The regex allows the global flag (g), which can replace the same key more than once. In the example above, you could use {name} multiple times in the same sentence and have them all replaced.

    – KrekkieD

    Apr 24, 2014 at 13:00


  • 3

    This seems awfully fragile, to be honest. What happens for instance if name is "blah {adjective} blah"?

    Jan 16, 2017 at 11:02

  • 7

    @ruffin “a little hyperbolic”? Code that is fooled into interpreting user data as format strings is an entire category of vulnerabilities. 98.44% is beyond mediocre.

    Jun 5, 2017 at 16:17


  • 3

    “If I had ever learnt, I should have been a great proficient.” – Lady Catherine de Bourgh. 🙂

    – mwardm

    Nov 8, 2017 at 15:45