JavaScript equivalent to printf/String.Format
Asked Answered
C

61

2433

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.

Colwin answered 4/3, 2009 at 12:53 Comment(7)
Aside all the great answers below, you may want to take a look at this one: https://mcmap.net/q/10176/-equivalent-of-string-format-in-jquery which IMO, is the most efficient solution to this problem.Canicula
I wrote a cheap one that uses C-like printf syntax.Pasia
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"Lignite
I have also written a simple function to achieve this; https://mcmap.net/q/10251/-string-format-not-work-in-typescriptCassimere
I have since released a fast and spec compliant printf implementation for Node.js and browser github.com/gajus/fast-printfLongoria
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.Unbeliever
Besides template string; String.padStart might be the other thing ppl looking for. (See #2687355 )Alcott
A
1631

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 simultaneous replacement instead like in fearphage’s suggestion.

Archaeo answered 4/3, 2009 at 12:53 Comment(15)
If only some simple number-to-string conversion is desired, num.toFixed() method might be enough!Moreta
@MaksymilianMajer that seems to be something massively different.Segregate
@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.Scolecite
@MaksymilianMajer right, just saying this answer is dead, and the link has decayed. It needs to be totally purged.Segregate
@Evan the link is fine and goes through.Worker
A simple perf test, its more elegant to use ''.format but it has poor performance; use when necessary. jsperf.com/string-format-mjamesToots
I wrote a near-perfect replica of PHP's sprintf function for JavaScript, please enjoy: github.com/Alhadis/Snippets/blob/master/js/str-sprintf.js ... please bear in mind it DOES extend the String prototype (e.g., invoke it by "%f".sprintf(0.5), etc). It has zero external dependencies and is pretty damn fast too. Only discrepancies from PHP's implementation would be more lenient error handling. :-)Imogene
fearphage's suggestion now handles this test correctly.Scirrhus
I think the best is _.template function from lodash or underscore.Conspire
This shouldn't be accepted answer anymore. As of ES6 this is built into the javascript language (both in browsers and NodeJS). See @Honeymoon 's answer below.Rhinal
@RyanShillington Unless you're not transpiling and are still supporting Internet Explorer (of any version), which some of us still are :(. Template strings are not supported by any version of IE and likely never will be. Remember that your html files in many templating engines aren't transpiled. I see lots of es6 still in inline code from folks who think es6 is ubiquitous. It's not... quite yet.Pyrone
Interpolated strings aren't a good solution for template strings that are not known at development time. For example, your page might retrieve a template string from a web server, then populate it with certain variables.Keratosis
This wouldn't work with constants.Touslesmois
If you would want to use more complex sprintf() placeholders such as %-5.2f, you can either use the above linked sprintf() JS implementation or use it's source as a cheat sheet to figure correct functions to call within the ES6 template syntax: github.com/alexei/sprintf.js/blob/master/src/sprintf.js#L69Frayda
But not so much powerfulHomogenize
A
1501

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}

Alboran answered 12/1, 2011 at 20:2 Comment(22)
the || trick doesn't work if args[number] is 0. Should do an explicit if() to see if (args[number] === undefined).Nephrotomy
in the else statement of the shorthand if, why not just do "match" instead of "'{' + number + '}'". match should equal that string.Scatterbrain
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.Blackheart
Great little function. The typeof check could be replaced with return number in args to make it even shorter.Ker
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.Alboran
Your proposed solution suffers from the same things as some of the others here. '{0}{1}'.format('{1}', '{0}') should return {1}{0}. Also yours seems to only be faster in Firefox.Alboran
Is there a way to make JSHint happy with the regex? "JSHint: Unescaped '{'."Challenge
I think this is better than sprintf() for JS because it does basically the same thing and it is very small.Hominoid
More benchmarks, building on what @RandomEngy furnished, and snippets from other answers to this question: jsperf.com/stringformat/6Leekgreen
How about something like String.format('function() {{ {0} }}', fnBody);? Currently, the result would still have the double braces, but what if I want to replace those with a single one? That would be more concise with the .NET version. Really like your solution though! Nice and small! :)Frore
Of course, a better example would be String.format('{{0}} was replaced with {0}', replcmnt);.Frore
Note that String.format("{{0}}", "a") will give you "{a}" instead of "{0}", as the .NET version would. Tested several codes here and found one that works way down the list.Yttriferous
I know this is old, but I just got to it. Love this function! Thanks! However, I just ran into a problem where is a replacement value is null, it will put the word null as the value. Is this intended behavior? If not, can it be fixed/modified?Internee
@Internee You can change the internal check to be less specific. Replace typeof args[number] != 'undefined' with args[number] != null. This will match both null and undefined values. If you never planned to pass in falsey values (0, empty string, boolean false, etc) you could remove ternary altogether and make it return args[number] || match.Alboran
I really like this and used it, but changed the If statement conditional check to be strict if (typeof String.prototype.format === "undefined") {, it is a personal preference. I also escaped the curly braces return this.replace(/\{(\d+)\}/g, function (match, number) {Dory
@dmasi Those are both valid suggestions for a much more strict environment. I won't be incorporating those, but I appreciate the suggestion.Alboran
this version works with objects too: function format(str, args) { if (arguments.length > 2) args = Array.prototype.slice.call(arguments, 1); if (typeof args !== 'object') args = [ args ]; return str.replace(/{(.*?)}/g, function(match, item) { return typeof args[item] != 'undefined' ? args[item] : match; }); };Kerb
Dont't use this solution in performance oriented code blocks. It is terribly slow. jsperf.com/string-format-vs-string-concatenationHowdoyoudo
Thanks for a great solution. I would add that for readability it might be worth using \w+ instead of \d+, and args = arguments[0]. This way you can have .format({'count': count, 'page': page, etc.}).Sideward
@Alboran : Probably a silly question, but shouldn't args be inaccessible within the scope of the anonymous function being passed to String.replace()? Would an arrow function not be required instead?Autrey
The most recent version of chrome is not going through this code so whenever i try to do format i'm getting format is not a function, So i warped the code inside a function and i'm calling it before calling format!Spindle
I think in Typescript, the signature could be change to this: String.format = function(format:string, args: string[]): string {. Then the Array.prototype line could be removed. Just a thoughtWalworth
P
654

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.

Peremptory answered 4/3, 2009 at 12:53 Comment(24)
It's pretty cool to answer a question on stackoverflow with code from stackoverflow, +1Leeleeann
Anyone understand why they're using a regex for the replace instead of just a string?Mccarthyism
@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.Phosphate
I think there is a typo: var args = typeof arguments[0] should be var arg = typeof arguments[0]Macnair
@Macnair there is no typo. He are just reusing the same variable to store the typeof and then, in the next line, the arguments.Pareu
@DineiA.Rockenbach but the code for (arg in args) will define a global variable named arg. If you're not using var arg at the first line, then you should use for(var arg in args) to avoid defining the global variable.Macnair
@DineiA.Rockenbach here is a demo jsbin.com/keluvukuka/edit?html,console,outputMacnair
This behaves weird if args[arg] contains $1, $' etc. The solution is to replace args[arg] with function() { return args[arg]; }.Inaccessible
This would be cooler if you could just put javascript expressions inside the curly braces and dispense with the names. c# 6.0 has this now.Emphasis
This method is much better than something like sprintf, since the named placeholders allow ordering. This is important if you outsource the initial string e. g. for localization. Imagine you have something like sprintf("Hello %s %s", ..., ...). In one language you want to place the forename first and then the surname, but in other language the surname first. The missing formatting is a disadvantage but one can still format the value in the params.Daydream
This seems awfully fragile, to be honest. What happens for instance if name is "blah {adjective} blah"?Bubble
@samhocevar Um, "Hello, blah OK blah, are you feeling OK?", natch. "Awfully" seems a little hyperbolic. If you wanted to code to ignore { in replacement values, you could easily or even make two passes if you want the more troublesome adjective = "blah {name} blah" to execute -- but boy, it does 98.44% of the job well as is, I believe.Pyrone
@Pyrone “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.Bubble
@samhocevar By assuming "user data", you've made this a much different conversation, one I don't see implied by the OP or this answer. As you're presenting, you might as well throw out String.replace too. ;^) I've given code that stops circular refs. Do you think SO is vulnerable somehow for using formatUnicorn? Or I'm missing your point entirely -- maybe you could submit an answer that improves on this one? (Btw, 99.44% (sorry for 98 typo earlier) is just a colloquialism)Pyrone
@Pyrone yes, stuff fed to {name} is typically user data. I do not know whether SO is vulnerable, but I do know they use a buggy function. (sadly, I came here looking for available options for string interpolation in JS and am thus unable to provide an improvement, but rest assured I would have had I been more literate in that language)Bubble
"If I had ever learnt, I should have been a great proficient." - Lady Catherine de Bourgh. :-)Melody
@samhocevar I can't believe you Little Bobby Tabled me. ;) If you're running text processed by client-side JavaScript on your database server without any safety checks, heaven help us all. ;^) Look, there shouldn't be anything any user can send from a client (eg, Postman) that gets past your server's security. And you should assume anything dangerous that could be sent from a client will be. That is, if you require 100% safety from client-side JavaScript code which is always user editable, and you think this function could open a security risk, you're playing in the wrong game.Pyrone
Too bad ReferenceError: formatUnicorn is not definedMonteux
Why can't we use 'arguments.slice()' rather than Array.prototype.slice.call(arguments)?Dip
@SarathSMenon Check this question or the explanation of "Array-like" here at MDN.Pyrone
Wow, this is exactly what is needed to format PHP PS3 logs client side using the message and context properties. Awesome. php-fig.org/psr/psr-3Ladonna
I believe this a npm package format-unicorn.Rahal
Try "{{good}".formatUnicorn({"good":"bad}","bad":"good"})Stob
if adjective is missing "Hello, {name}, are you feeling {adjective}?".formatUnicorn({name:"Gabriel"}); it returns "Hello, Gabriel, are you feeling {adjective}?", expected return is "Hello, Gabriel are you feeling ?" How can I achieve that?Herculaneum
P
359

Number Formatting in JavaScript

I got to this question page hoping to find how to format numbers in JavaScript, without introducing yet another library. Here's what I've found:

Rounding floating-point numbers

The equivalent of sprintf("%.2f", num) in JavaScript seems to be num.toFixed(2), which formats num to 2 decimal places, with rounding (but see @ars265's comment about Math.round below).

(12.345).toFixed(2); // returns "12.35" (rounding!)
(12.3).toFixed(2); // returns "12.30" (zero padding)

Exponential form

The equivalent of sprintf("%.2e", num) is num.toExponential(2).

(33333).toExponential(2); // "3.33e+4"

Hexadecimal and other bases

To print numbers in base B, try num.toString(B). JavaScript supports automatic conversion to and from bases 2 through 36 (in addition, some browsers have limited support for base64 encoding).

(3735928559).toString(16); // to base 16: "deadbeef"
parseInt("deadbeef", 16); // from base 16: 3735928559

Reference Pages

Quick tutorial on JS number formatting

Mozilla reference page for toFixed() (with links to toPrecision(), toExponential(), toLocaleString(), ...)

Piper answered 4/3, 2009 at 12:53 Comment(7)
Wouldn't it just be better to enclose the number literal in parenthesis, instead of leaving a weird white space there?Galvanotropism
That would probably look better, true. But my goal there is just to point out the syntax error trap.Piper
Just a side note if you're using an older browser, or supporting older browsers, some browsers implemented toFixed incorrectly, using Math.round in place of toFixed is a better solution.Chaulmoogra
@Raphael_ and @rescdsk: .. also works: 33333..toExponential(2);Patiencepatient
Or (33333).toExponential(2)Winterfeed
@ars265: If I understand you right, typical usage would be Math.round(100*x)/100 to get x with 2 digits after the decimal point. This, however, returns a number, so then you are stuck with the number formatting problem afterwards, for all rational numbers that cannot be exactly represented in both binary and decimal form. In contrast, number.toFixed(d) takes care of the formatting and returns a string. BTW, today I used a trick for padding integers with leading zeroes: s = (n/1000).toFixed(3).substring(2); (works for integers below 1000) :-)Marrissa
Relevant to this concern and technique is developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…. For example: String(4).padStart(5,'0') gives '00004'.Peacoat
H
315

From ES6 on you could use template strings:

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

Be aware that template strings are surrounded by backticks ` instead of (single) quotes.

Note that the string is expanded immediately as soon as you define the string.

For further information:

https://developers.google.com/web/updates/2015/01/ES6-Template-Strings

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/template_strings

Note: Check the mozilla-site to find a list of supported browsers.

Honeymoon answered 4/3, 2009 at 12:53 Comment(10)
The problem with template strings is that they seem to be executed immediately, making their use as, say, an i18n-like string table completely worthless. I can't define the string early on, and supply the parameters to use later and/or repeatedly.Weldonwelfare
@Weldonwelfare You're right that they're not built to be assigned to a variable, which is a bit mind-warping, but it's easy enough to work with templated strings' instant-execution tendancies if you hide them in a function. See jsfiddle.net/zvcm70paMyocardium
@Weldonwelfare there is no difference between using a template string or old style string concatenation, its sugar for the same thing. You would have to wrap an old style string generator in a simple function and the same thing works fine with string templates. const compile = (x, y) => `I can call this template string whenever I want.. x=${x}, y=${y}` ... compile(30, 20)Mcgowen
Actually, I've since looked at the Mozilla link given in this answer and see that their example tagged template that returns a function isn't a bad way to do i18n-like string tables, where you can pass the returned function the parameters you want to put into the string. Granted, I don't know how that compares in memory size to simply strings you interpolate by replacing {0} markers.Weldonwelfare
this solution won't work for format string passed in variable (from server for example)Gyve
@Myocardium That works fine if your template function is defined on the same machine where it is executed. As far as I know, you can't pass a function as JSON, so storing template functions in a database doesn't work well.Gaylegayleen
+1 for using the standard built in function which can be helpful in a lot of situations. here a list of polyfills for supporting older browsersPledget
caniuse.com/#feat=template-literals suggests there are still >10% of net denizens using browsers that don't support this. Does it degrade nicely, or explode?Frisse
Template strings do not feature the formatting capabilities, hence why they're uncomparable in that regard. Ex. specifying argument width, or making sure that argument has exact precision.Tarbox
It's nothing close to sprintf. In sprintf, I can use, say, %.2f to print money - but template string is just a dumb wrapper around string concatenation...Christiniachristis
F
182

jsxt, Zippo

This option fits better.

String.prototype.format = function() {
    var formatted = this;
    for (var i = 0; i < arguments.length; i++) {
        var regexp = new RegExp('\\{'+i+'\\}', 'gi');
        formatted = formatted.replace(regexp, arguments[i]);
    }
    return formatted;
};

With this option I can replace strings like these:

'The {0} is dead. Don\'t code {0}. Code {1} that is open source!'.format('ASP', 'PHP');

With your code the second {0} wouldn't be replaced. ;)

Featherstone answered 4/3, 2009 at 12:53 Comment(3)
gist.github.com/1049426 I updated your example with this approach. Numerous benefits including saving the native implementation if it exists, stringifying, etc. I tried removing regular expressions, but welp kind of needed for global replace. :-/Agnesagnese
jsxt is GPL-licensed unfortunatelyWiretap
Very inefficient approach. Uses regex when not needed, looks up a whole string for searching many times.Bilbrey
T
129

For Node.js users there is util.format which has printf-like functionality:

util.format("%s world", "Hello")
Tallow answered 4/3, 2009 at 12:53 Comment(4)
This doesn't support %x as of Node v0.10.26Cott
Doesn't support width and alignment modifiers either (e.g. %-20s %5.2f)Maryettamaryjane
I had to scroll all the way down the page to see this useful answer.Knobloch
From util.format node documentation: "util.format() is a synchronous method that is intended as a debugging tool. Some input values can have a significant performance overhead that can block the event loop. Use this function with care and never in a hot code path."Composure
L
120

I use this simple function:

String.prototype.format = function() {
    var formatted = this;
    for( var arg in arguments ) {
        formatted = formatted.replace("{" + arg + "}", arguments[arg]);
    }
    return formatted;
};

That's very similar to string.format:

"{0} is dead, but {1} is alive!".format("ASP", "ASP.NET")
Levina answered 4/3, 2009 at 12:53 Comment(11)
What about "{{0}} use this next instead {0}"? Not really a String.Format, but it is small and nice function to use.Delivery
why +=?, should it formatted = this.replace("{" + arg + "}", arguments[arg]);Ratio
It shouldn't be =, it should be +=, because arguments[arg] may not be a string. Because format is already a string outside the loop, + will concatenate the string, and arguments[arg] will automatically have .toString() called on it. If arguments[0] was 5 and arguments[1] was 7, then instead of "{1}{2}".format(5,7) returning 57, the first argument will yield 12 and the second one will throw an out of bounds exception.Cassandracassandre
Ahh, but now I see he's adding the whole replacement to the string every time, you're right. user437231's answer is correct. For some reason I was thinking this.replace was only returning the replaced part...Cassandracassandre
I think the code is still not correct. The correct one one should be like Filipiz posted.Actinopod
For reference, for...in won't work in every browser as this code expects it to. It'll loop over all enumerable properties, which in some browsers will include arguments.length, and in others won't even include the arguments themselves at all. In any case, if Object.prototype is added to, any additions will probably be included in the bunch. The code should be using a standard for loop, rather than for...in.Supergalaxy
This fails if a previous replacement contains a format string as well: "{0} is dead, but {1} is alive!".format("{1}", "ASP.NET") === "ASP.NET is dead, but ASP.NET is alive!"Archaeo
The variable arg is global. You need to do this instead: for (var arg in arguments) {Sassafras
formatted.replace(new RegExp("\\{" + arg + "\\}", "g"), arguments[arg]) allows you to repeat indexes in case you need to duplicate values in the string.Typecast
The function is unsafe as it sometimes interprets {n}s within the strings passed to it: '{0}'.format('you should only see this {1}', 'you should not see this')Ginaginder
CHANGE formatted = formatted.replace("{" + arg + "}", arguments[arg]); TO formatted = formatted.split('{' + arg + '}').join(arguments[arg]);Twinkle
C
68

I'm surprised no one used reduce, this is a native concise and powerful JavaScript function.

ES6 (EcmaScript2015)

String.prototype.format = function() {
  return [...arguments].reduce((p,c) => p.replace(/%s/,c), this);
};

console.log('Is that a %s or a %s?... No, it\'s %s!'.format('plane', 'bird', 'SOman'));

< ES6

function interpolate(theString, argumentArray) {
    var regex = /%s/;
    var _r=function(p,c){return p.replace(regex,c);}
    return argumentArray.reduce(_r, theString);
}

interpolate("%s, %s and %s", ["Me", "myself", "I"]); // "Me, myself and I"

How it works:

reduce applies a function against an accumulator and each element in the array (from left to right) to reduce it to a single value.

var _r= function(p,c){return p.replace(/%s/,c)};

console.log(
  ["a", "b", "c"].reduce(_r, "[%s], [%s] and [%s]") + '\n',
  [1, 2, 3].reduce(_r, "%s+%s=%s") + '\n',
  ["cool", 1337, "stuff"].reduce(_r, "%s %s %s")
);
Coenurus answered 4/3, 2009 at 12:53 Comment(4)
Here's a version that uses this approach to create a simplified printf function: jsfiddle.net/11szrbx9Kirkwood
And here is another one using ES6, in one line: (...a) => {return a.reduce((p: string, c: any) => p.replace(/%s/, c));Asur
No need for String.prototype.format in ES6: ((a,b,c)=>`${a}, ${b} and ${c}`)(...['me', 'myself', 'I']) (note that this is a bit redundant to better fit in your example)Holton
You'd have to implement replacement functions for each of printf's type specifiers and include logic for padding prefixes. Iterating over the format string in a sensible fashion seems to be the minor challenge here, imho. Neat solution if you only need string replacements, though.Kyliekylila
H
54

Here's a minimal implementation of sprintf in JavaScript: it only does "%s" and "%d", but I have left space for it to be extended. It is useless to the OP, but other people who stumble across this thread coming from Google might benefit from it.

function sprintf() {
    var args = arguments,
    string = args[0],
    i = 1;
    return string.replace(/%((%)|s|d)/g, function (m) {
        // m is the matched format, e.g. %s, %d
        var val = null;
        if (m[2]) {
            val = m[2];
        } else {
            val = args[i];
            // A switch statement so that the formatter can be extended. Default is %s
            switch (m) {
                case '%d':
                    val = parseFloat(val);
                    if (isNaN(val)) {
                        val = 0;
                    }
                    break;
            }
            i++;
        }
        return val;
    });
}

Example:

alert(sprintf('Latitude: %s, Longitude: %s, Count: %d', 41.847, -87.661, 'two'));
// Expected output: Latitude: 41.847, Longitude: -87.661, Count: 0

In contrast with similar solutions in previous replies, this one does all substitutions in one go, so it will not replace parts of previously replaced values.

Heigho answered 4/3, 2009 at 12:53 Comment(1)
any idea how to suit for %02d?Griffie
L
34

3 different ways to format javascript string

There are 3 different ways to format a string by replacing placeholders with the variable value.

  1. Using template literal (backticks ``)

    let name = 'John';
    let age = 30;
    // using backticks
    console.log(`${name} is ${age} years old.`);
    // John is 30 years old.
  2. Using concatenation

let name = 'John';
let age = 30;
// using concatenation
console.log(name + ' is ' + age + ' years old.');
// John is 30 years old.
  1. Creating own format function

String.prototype.format = function () {
  var args = arguments;
  return this.replace(/{([0-9]+)}/g, function (match, index) {
    // check if the argument is there
    return typeof args[index] == 'undefined' ? match : args[index];
  });
};


console.log('{0} is {1} years old.'.format('John', 30));
Lorica answered 4/3, 2009 at 12:53 Comment(3)
Thanks! 3rd option suits for my case.Fallonfallout
3rd option is the only one that allows variables to be "injected" AFTER declaring the string.Marine
The third option is called "monkey patching" and this is generally considered an antipattern, generally only to be used when backporting a feature (like Array.prototype.forEach) to an older engine. See the "Warning" in article on MDN. While not a completely symmetric with printf, util.format is probably the best choice for what the op is looking for.Thorough
P
32

JavaScript programmers can use String.prototype.sprintf at https://github.com/ildar-shaimordanov/jsxt/blob/master/js/String.js. Below is example:

var d = new Date();
var dateStr = '%02d:%02d:%02d'.sprintf(
    d.getHours(), 
    d.getMinutes(), 
    d.getSeconds());
Peng answered 4/3, 2009 at 12:53 Comment(0)
P
28

Adding to zippoxer's answer, I use this function:

String.prototype.format = function () {
    var a = this, b;
    for (b in arguments) {
        a = a.replace(/%[a-z]/, arguments[b]);
    }
    return a; // Make chainable
};

var s = 'Hello %s The magic number is %d.';
s.format('world!', 12); // Hello World! The magic number is 12.

I also have a non-prototype version which I use more often for its Java-like syntax:

function format() {
    var a, b, c;
    a = arguments[0];
    b = [];
    for(c = 1; c < arguments.length; c++){
        b.push(arguments[c]);
    }
    for (c in b) {
        a = a.replace(/%[a-z]/, b[c]);
    }
    return a;
}
format('%d ducks, 55 %s', 12, 'cats'); // 12 ducks, 55 cats

ES 2015 update

All the cool new stuff in ES 2015 makes this a lot easier:

function format(fmt, ...args){
    return fmt
        .split("%%")
        .reduce((aggregate, chunk, i) =>
            aggregate + chunk + (args[i] || ""), "");
}

format("Hello %%! I ate %% apples today.", "World", 44);
// "Hello World, I ate 44 apples today."

I figured that since this, like the older ones, doesn't actually parse the letters, it might as well just use a single token %%. This has the benefit of being obvious and not making it difficult to use a single %. However, if you need %% for some reason, you would need to replace it with itself:

format("I love percentage signs! %%", "%%");
// "I love percentage signs! %%"
Pasia answered 4/3, 2009 at 12:53 Comment(2)
this answer was great for a quick copy paste into an existing function. No require no downloads etc.Deweydewhirst
The only answer that can satisfy. IDK why people suggesting template literals. printf/sprintf doesn't limit the arguments, but when using template literals, I have to know the variables and those are not fixed. Common sense is rarely common! sorry for being rude! 😑Homerus
F
28

I want to share my solution for the 'problem'. I haven't re-invented the wheel but tries to find a solution based on what JavaScript already does. The advantage is, that you get all implicit conversions for free. Setting the prototype property $ of String gives a very nice and compact syntax (see examples below). It is maybe not the most efficient way, but in most cases dealing with output it does not have to be super optimized.

String.form = function(str, arr) {
    var i = -1;
    function callback(exp, p0, p1, p2, p3, p4) {
        if (exp=='%%') return '%';
        if (arr[++i]===undefined) return undefined;
        exp  = p2 ? parseInt(p2.substr(1)) : undefined;
        var base = p3 ? parseInt(p3.substr(1)) : undefined;
        var val;
        switch (p4) {
            case 's': val = arr[i]; break;
            case 'c': val = arr[i][0]; break;
            case 'f': val = parseFloat(arr[i]).toFixed(exp); break;
            case 'p': val = parseFloat(arr[i]).toPrecision(exp); break;
            case 'e': val = parseFloat(arr[i]).toExponential(exp); break;
            case 'x': val = parseInt(arr[i]).toString(base?base:16); break;
            case 'd': val = parseFloat(parseInt(arr[i], base?base:10).toPrecision(exp)).toFixed(0); break;
        }
        val = typeof(val)=='object' ? JSON.stringify(val) : val.toString(base);
        var sz = parseInt(p1); /* padding size */
        var ch = p1 && p1[0]=='0' ? '0' : ' '; /* isnull? */
        while (val.length<sz) val = p0 !== undefined ? val+ch : ch+val; /* isminus? */
       return val;
    }
    var regex = /%(-)?(0?[0-9]+)?([.][0-9]+)?([#][0-9]+)?([scfpexd%])/g;
    return str.replace(regex, callback);
}

String.prototype.$ = function() {
    return String.form(this, Array.prototype.slice.call(arguments));
}

Here are a few examples:

String.format("%s %s", [ "This is a string", 11 ])
console.log("%s %s".$("This is a string", 11))
var arr = [ "12.3", 13.6 ]; console.log("Array: %s".$(arr));
var obj = { test:"test", id:12 }; console.log("Object: %s".$(obj));
console.log("%c", "Test");
console.log("%5d".$(12)); // '   12'
console.log("%05d".$(12)); // '00012'
console.log("%-5d".$(12)); // '12   '
console.log("%5.2d".$(123)); // '  120'
console.log("%5.2f".$(1.1)); // ' 1.10'
console.log("%10.2e".$(1.1)); // '   1.10e+0'
console.log("%5.3p".$(1.12345)); // ' 1.12'
console.log("%5x".$(45054)); // ' affe'
console.log("%20#2x".$("45054")); // '    1010111111111110'
console.log("%6#2d".$("111")); // '     7'
console.log("%6#16d".$("affe")); // ' 45054'
Freemon answered 4/3, 2009 at 12:53 Comment(1)
unfortunately at least # and + are not implemented for floats. here is a reference for the function in c: tutorialspoint.com/c_standard_library/c_function_sprintf.htmKeratosis
L
20

+1 Zippo with the exception that the function body needs to be as below or otherwise it appends the current string on every iteration:

String.prototype.format = function() {
    var formatted = this;
    for (var arg in arguments) {
        formatted = formatted.replace("{" + arg + "}", arguments[arg]);
    }
    return formatted;
};
Lewiss answered 4/3, 2009 at 12:53 Comment(4)
It didn't work on Firefox. The debugger show arg as undefined.Kyungkyushu
It does not replace the second character 'The {0} is dead. Don\'t code {0}. Code {1} that is open source!'.format('ASP', 'PHP'); the result becomes The ASP is dead. Don't code {0}. Code PHP that is open source!. One more thing for(arg in arguments) does not work in IE. i replaced with for (arg = 0; arg <arguments.length; arg++)Didactics
For reference, for...in won't work in every browser as this code expects it to. It'll loop over all enumerable properties, which in some browsers will include arguments.length, and in others won't even include the arguments themselves at all. In any case, if Object.prototype is added to, any additions will probably be included in the bunch. The code should be using a standard for loop, rather than for...in.Supergalaxy
You should propose an answer edit instead of duplicate answer. This duplicate this answerAirdrie
S
15

I use a small library called String.format for JavaScript which supports most of the format string capabilities (including format of numbers and dates), and uses the .NET syntax. The script itself is smaller than 4 kB, so it doesn't create much of overhead.

Supersonics answered 4/3, 2009 at 12:53 Comment(5)
I took a look at that library and it looks really great. I was pissed off when I saw that the download was an EXE. What the heck is that about? Didn't download.Thomasina
Often a downloadable archive that's an EXE is nothing more than a "self-extracting ZIP". Execute it, and it will unpack itself. This is quite convenient BUT because it looks so much like malware, the format is not used on the web all that often any more.Sideswipe
While this link may answer the question, it is better to include the essential parts of the answer here and provide the link for reference. Link-only answers can become invalid if the linked page changes.Venial
@Venial the link is to a (minified) 4 kB javascript library. I don't believe pasting it into the answer is a good idea.Endorse
You are right pasting it would be no better. I just got this comment for random review - and commented before disliking it. To me stackoverflow is better when providing explanations rather than ready made solutions (which the link is). I also do not want to encourage people to post or download black-box code.Venial
C
14

I'll add my own discoveries which I've found since I asked:

Sadly it seems sprintf doesn't handle thousand separator formatting like .NET's string format.

Colwin answered 4/3, 2009 at 12:53 Comment(0)
N
13

Very elegant:

String.prototype.format = function (){
    var args = arguments;
    return this.replace(/\{\{|\}\}|\{(\d+)\}/g, function (curlyBrack, index) {
        return ((curlyBrack == "{{") ? "{" : ((curlyBrack == "}}") ? "}" : args[index]));
    });
};

// Usage:
"{0}{1}".format("{1}", "{0}")

Credit goes to (broken link) https://gist.github.com/0i0/1519811

Nubbly answered 4/3, 2009 at 12:53 Comment(1)
This is the only one that handles escape brackets {{0}} as well as things like {0}{1}.format("{1}", "{0}"). Should be at the very top!Yttriferous
B
13

If you are looking to handle the thousands separator, you should really use toLocaleString() from the JavaScript Number class since it will format the string for the user's region.

The JavaScript Date class can format localized dates and times.

Berte answered 4/3, 2009 at 12:53 Comment(2)
It's actually a set by the user as a setting in the application (not the machine their on) but I'll take a look, thanksColwin
add some examples so that everyone can understands it quickly.Aboriginal
G
10

The PHPJS project has written JavaScript implementations for many of PHP's functions. Since PHP's sprintf() function is basically the same as C's printf(), their JavaScript implementation of it should satisfy your needs.

Goatee answered 4/3, 2009 at 12:53 Comment(0)
U
9

I have a solution very close to Peter's, but it deals with number and object case.

if (!String.prototype.format) {
  String.prototype.format = function() {
    var args;
    args = arguments;
    if (args.length === 1 && args[0] !== null && typeof args[0] === 'object') {
      args = args[0];
    }
    return this.replace(/{([^}]*)}/g, function(match, key) {
      return (typeof args[key] !== "undefined" ? args[key] : match);
    });
  };
}

Maybe it could be even better to deal with the all deeps cases, but for my needs this is just fine.

"This is an example from {name}".format({name:"Blaine"});
"This is an example from {0}".format("Blaine");

PS: This function is very cool if you are using translations in templates frameworks like AngularJS:

<h1> {{('hello-message'|translate).format(user)}} <h1>
<h1> {{('hello-by-name'|translate).format( user ? user.name : 'You' )}} <h1>

Where the en.json is something like

{
    "hello-message": "Hello {name}, welcome.",
    "hello-by-name": "Hello {0}, welcome."
}
Untraveled answered 4/3, 2009 at 12:53 Comment(1)
the [^}] part in the regexp is unnecesary.. use {(.*?)} instead, or better {([\s\S]*?)} to match newline too.Erzurum
I
9

I use this one:

String.prototype.format = function() {
    var newStr = this, i = 0;
    while (/%s/.test(newStr))
        newStr = newStr.replace("%s", arguments[i++])

    return newStr;
}

Then I call it:

"<h1>%s</h1><p>%s</p>".format("Header", "Just a test!");
Immune answered 4/3, 2009 at 12:53 Comment(0)
S
9

There is "sprintf" for JavaScript which you can find at http://www.webtoolkit.info/javascript-sprintf.html.

Smithson answered 4/3, 2009 at 12:53 Comment(0)
M
8

One very slightly different version, the one I prefer (this one uses {xxx} tokens rather than {0} numbered arguments, this is much more self-documenting and suits localization much better):

String.prototype.format = function(tokens) {
  var formatted = this;
  for (var token in tokens)
    if (tokens.hasOwnProperty(token))
      formatted = formatted.replace(RegExp("{" + token + "}", "g"), tokens[token]);
  return formatted;
};

A variation would be:

  var formatted = l(this);

that calls an l() localization function first.

Maciemaciel answered 4/3, 2009 at 12:53 Comment(0)
C
6

We can use a simple lightweight String.Format string operation library for Typescript.

String.Format():

var id = image.GetId()
String.Format("image_{0}.jpg", id)
output: "image_2db5da20-1c5d-4f1a-8fd4-b41e34c8c5b5.jpg";

String Format for specifiers:

var value = String.Format("{0:L}", "APPLE"); //output "apple"

value = String.Format("{0:U}", "apple"); // output "APPLE"

value = String.Format("{0:d}", "2017-01-23 00:00"); //output "23.01.2017"


value = String.Format("{0:s}", "21.03.2017 22:15:01") //output "2017-03-21T22:15:01"

value = String.Format("{0:n}", 1000000);
//output "1.000.000"

value = String.Format("{0:00}", 1);
//output "01"

String Format for Objects including specifiers:

var fruit = new Fruit();
fruit.type = "apple";
fruit.color = "RED";
fruit.shippingDate = new Date(2018, 1, 1);
fruit.amount = 10000;

String.Format("the {type:U} is {color:L} shipped on {shippingDate:s} with an amount of {amount:n}", fruit);
// output: the APPLE is red shipped on 2018-01-01 with an amount of 10.000
Calondra answered 4/3, 2009 at 12:53 Comment(0)
S
6

For basic formatting:

var template = jQuery.validator.format("{0} is not a valid value");
var result = template("abc");
Stoltz answered 4/3, 2009 at 12:53 Comment(0)
K
5

Using Lodash you can get template functionality:

Use the ES template literal delimiter as an "interpolate" delimiter. Disable support by replacing the "interpolate" delimiter.

var compiled = _.template('hello ${ user }!');
compiled({ 'user': 'pebbles' });
// => 'hello pebbles!
Koval answered 4/3, 2009 at 12:53 Comment(0)
C
5

For those who like Node.JS and its util.format feature, I've just extracted it out into its vanilla JavaScript form (with only functions that util.format uses):

exports = {};

function isString(arg) {
    return typeof arg === 'string';
}
function isNull(arg) {
    return arg === null;
}
function isObject(arg) {
    return typeof arg === 'object' && arg !== null;
}
function isBoolean(arg) {
    return typeof arg === 'boolean';
}
function isUndefined(arg) {
    return arg === void 0;
}
function stylizeNoColor(str, styleType) {
    return str;
}
function stylizeWithColor(str, styleType) {
    var style = inspect.styles[styleType];

    if (style) {
        return '\u001b[' + inspect.colors[style][0] + 'm' + str +
            '\u001b[' + inspect.colors[style][3] + 'm';
    } else {
        return str;
    }
}
function isFunction(arg) {
    return typeof arg === 'function';
}
function isNumber(arg) {
    return typeof arg === 'number';
}
function isSymbol(arg) {
    return typeof arg === 'symbol';
}
function formatPrimitive(ctx, value) {
    if (isUndefined(value))
        return ctx.stylize('undefined', 'undefined');
    if (isString(value)) {
        var simple = '\'' + JSON.stringify(value).replace(/^"|"$/g, '')
                .replace(/'/g, "\\'")
                .replace(/\\"/g, '"') + '\'';
        return ctx.stylize(simple, 'string');
    }
    if (isNumber(value)) {
        // Format -0 as '-0'. Strict equality won't distinguish 0 from -0,
        // so instead we use the fact that 1 / -0 < 0 whereas 1 / 0 > 0 .
        if (value === 0 && 1 / value < 0)
            return ctx.stylize('-0', 'number');
        return ctx.stylize('' + value, 'number');
    }
    if (isBoolean(value))
        return ctx.stylize('' + value, 'boolean');
    // For some reason typeof null is "object", so special case here.
    if (isNull(value))
        return ctx.stylize('null', 'null');
    // es6 symbol primitive
    if (isSymbol(value))
        return ctx.stylize(value.toString(), 'symbol');
}
function arrayToHash(array) {
    var hash = {};

    array.forEach(function (val, idx) {
        hash[val] = true;
    });

    return hash;
}
function objectToString(o) {
    return Object.prototype.toString.call(o);
}
function isDate(d) {
    return isObject(d) && objectToString(d) === '[object Date]';
}
function isError(e) {
    return isObject(e) &&
        (objectToString(e) === '[object Error]' || e instanceof Error);
}
function isRegExp(re) {
    return isObject(re) && objectToString(re) === '[object RegExp]';
}
function formatError(value) {
    return '[' + Error.prototype.toString.call(value) + ']';
}
function formatPrimitiveNoColor(ctx, value) {
    var stylize = ctx.stylize;
    ctx.stylize = stylizeNoColor;
    var str = formatPrimitive(ctx, value);
    ctx.stylize = stylize;
    return str;
}
function isArray(ar) {
    return Array.isArray(ar);
}
function hasOwnProperty(obj, prop) {
    return Object.prototype.hasOwnProperty.call(obj, prop);
}
function formatProperty(ctx, value, recurseTimes, visibleKeys, key, array) {
    var name, str, desc;
    desc = Object.getOwnPropertyDescriptor(value, key) || {value: value[key]};
    if (desc.get) {
        if (desc.set) {
            str = ctx.stylize('[Getter/Setter]', 'special');
        } else {
            str = ctx.stylize('[Getter]', 'special');
        }
    } else {
        if (desc.set) {
            str = ctx.stylize('[Setter]', 'special');
        }
    }
    if (!hasOwnProperty(visibleKeys, key)) {
        name = '[' + key + ']';
    }
    if (!str) {
        if (ctx.seen.indexOf(desc.value) < 0) {
            if (isNull(recurseTimes)) {
                str = formatValue(ctx, desc.value, null);
            } else {
                str = formatValue(ctx, desc.value, recurseTimes - 1);
            }
            if (str.indexOf('\n') > -1) {
                if (array) {
                    str = str.split('\n').map(function (line) {
                        return '  ' + line;
                    }).join('\n').substr(2);
                } else {
                    str = '\n' + str.split('\n').map(function (line) {
                        return '   ' + line;
                    }).join('\n');
                }
            }
        } else {
            str = ctx.stylize('[Circular]', 'special');
        }
    }
    if (isUndefined(name)) {
        if (array && key.match(/^\d+$/)) {
            return str;
        }
        name = JSON.stringify('' + key);
        if (name.match(/^"([a-zA-Z_][a-zA-Z_0-9]*)"$/)) {
            name = name.substr(1, name.length - 2);
            name = ctx.stylize(name, 'name');
        } else {
            name = name.replace(/'/g, "\\'")
                .replace(/\\"/g, '"')
                .replace(/(^"|"$)/g, "'")
                .replace(/\\\\/g, '\\');
            name = ctx.stylize(name, 'string');
        }
    }

    return name + ': ' + str;
}
function formatArray(ctx, value, recurseTimes, visibleKeys, keys) {
    var output = [];
    for (var i = 0, l = value.length; i < l; ++i) {
        if (hasOwnProperty(value, String(i))) {
            output.push(formatProperty(ctx, value, recurseTimes, visibleKeys,
                String(i), true));
        } else {
            output.push('');
        }
    }
    keys.forEach(function (key) {
        if (!key.match(/^\d+$/)) {
            output.push(formatProperty(ctx, value, recurseTimes, visibleKeys,
                key, true));
        }
    });
    return output;
}
function reduceToSingleString(output, base, braces) {
    var length = output.reduce(function (prev, cur) {
        return prev + cur.replace(/\u001b\[\d\d?m/g, '').length + 1;
    }, 0);

    if (length > 60) {
        return braces[0] +
            (base === '' ? '' : base + '\n ') +
            ' ' +
            output.join(',\n  ') +
            ' ' +
            braces[1];
    }

    return braces[0] + base + ' ' + output.join(', ') + ' ' + braces[1];
}
function formatValue(ctx, value, recurseTimes) {
    // Provide a hook for user-specified inspect functions.
    // Check that value is an object with an inspect function on it
    if (ctx.customInspect &&
        value &&
        isFunction(value.inspect) &&
            // Filter out the util module, it's inspect function is special
        value.inspect !== exports.inspect &&
            // Also filter out any prototype objects using the circular check.
        !(value.constructor && value.constructor.prototype === value)) {
        var ret = value.inspect(recurseTimes, ctx);
        if (!isString(ret)) {
            ret = formatValue(ctx, ret, recurseTimes);
        }
        return ret;
    }

    // Primitive types cannot have properties
    var primitive = formatPrimitive(ctx, value);
    if (primitive) {
        return primitive;
    }

    // Look up the keys of the object.
    var keys = Object.keys(value);
    var visibleKeys = arrayToHash(keys);

    if (ctx.showHidden) {
        keys = Object.getOwnPropertyNames(value);
    }

    // This could be a boxed primitive (new String(), etc.), check valueOf()
    // NOTE: Avoid calling `valueOf` on `Date` instance because it will return
    // a number which, when object has some additional user-stored `keys`,
    // will be printed out.
    var formatted;
    var raw = value;
    try {
        // the .valueOf() call can fail for a multitude of reasons
        if (!isDate(value))
            raw = value.valueOf();
    } catch (e) {
        // ignore...
    }

    if (isString(raw)) {
        // for boxed Strings, we have to remove the 0-n indexed entries,
        // since they just noisey up the output and are redundant
        keys = keys.filter(function (key) {
            return !(key >= 0 && key < raw.length);
        });
    }

    // Some type of object without properties can be shortcutted.
    if (keys.length === 0) {
        if (isFunction(value)) {
            var name = value.name ? ': ' + value.name : '';
            return ctx.stylize('[Function' + name + ']', 'special');
        }
        if (isRegExp(value)) {
            return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp');
        }
        if (isDate(value)) {
            return ctx.stylize(Date.prototype.toString.call(value), 'date');
        }
        if (isError(value)) {
            return formatError(value);
        }
        // now check the `raw` value to handle boxed primitives
        if (isString(raw)) {
            formatted = formatPrimitiveNoColor(ctx, raw);
            return ctx.stylize('[String: ' + formatted + ']', 'string');
        }
        if (isNumber(raw)) {
            formatted = formatPrimitiveNoColor(ctx, raw);
            return ctx.stylize('[Number: ' + formatted + ']', 'number');
        }
        if (isBoolean(raw)) {
            formatted = formatPrimitiveNoColor(ctx, raw);
            return ctx.stylize('[Boolean: ' + formatted + ']', 'boolean');
        }
    }

    var base = '', array = false, braces = ['{', '}'];

    // Make Array say that they are Array
    if (isArray(value)) {
        array = true;
        braces = ['[', ']'];
    }

    // Make functions say that they are functions
    if (isFunction(value)) {
        var n = value.name ? ': ' + value.name : '';
        base = ' [Function' + n + ']';
    }

    // Make RegExps say that they are RegExps
    if (isRegExp(value)) {
        base = ' ' + RegExp.prototype.toString.call(value);
    }

    // Make dates with properties first say the date
    if (isDate(value)) {
        base = ' ' + Date.prototype.toUTCString.call(value);
    }

    // Make error with message first say the error
    if (isError(value)) {
        base = ' ' + formatError(value);
    }

    // Make boxed primitive Strings look like such
    if (isString(raw)) {
        formatted = formatPrimitiveNoColor(ctx, raw);
        base = ' ' + '[String: ' + formatted + ']';
    }

    // Make boxed primitive Numbers look like such
    if (isNumber(raw)) {
        formatted = formatPrimitiveNoColor(ctx, raw);
        base = ' ' + '[Number: ' + formatted + ']';
    }

    // Make boxed primitive Booleans look like such
    if (isBoolean(raw)) {
        formatted = formatPrimitiveNoColor(ctx, raw);
        base = ' ' + '[Boolean: ' + formatted + ']';
    }

    if (keys.length === 0 && (!array || value.length === 0)) {
        return braces[0] + base + braces[1];
    }

    if (recurseTimes < 0) {
        if (isRegExp(value)) {
            return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp');
        } else {
            return ctx.stylize('[Object]', 'special');
        }
    }

    ctx.seen.push(value);

    var output;
    if (array) {
        output = formatArray(ctx, value, recurseTimes, visibleKeys, keys);
    } else {
        output = keys.map(function (key) {
            return formatProperty(ctx, value, recurseTimes, visibleKeys, key, array);
        });
    }

    ctx.seen.pop();

    return reduceToSingleString(output, base, braces);
}
function inspect(obj, opts) {
    // default options
    var ctx = {
        seen: [],
        stylize: stylizeNoColor
    };
    // legacy...
    if (arguments.length >= 3) ctx.depth = arguments[2];
    if (arguments.length >= 4) ctx.colors = arguments[3];
    if (isBoolean(opts)) {
        // legacy...
        ctx.showHidden = opts;
    } else if (opts) {
        // got an "options" object
        exports._extend(ctx, opts);
    }
    // set default options
    if (isUndefined(ctx.showHidden)) ctx.showHidden = false;
    if (isUndefined(ctx.depth)) ctx.depth = 2;
    if (isUndefined(ctx.colors)) ctx.colors = false;
    if (isUndefined(ctx.customInspect)) ctx.customInspect = true;
    if (ctx.colors) ctx.stylize = stylizeWithColor;
    return formatValue(ctx, obj, ctx.depth);
}
exports.inspect = inspect;


// http://en.wikipedia.org/wiki/ANSI_escape_code#graphics
inspect.colors = {
    'bold': [1, 22],
    'italic': [3, 23],
    'underline': [4, 24],
    'inverse': [7, 27],
    'white': [37, 39],
    'grey': [90, 39],
    'black': [30, 39],
    'blue': [34, 39],
    'cyan': [36, 39],
    'green': [32, 39],
    'magenta': [35, 39],
    'red': [31, 39],
    'yellow': [33, 39]
};

// Don't use 'blue' not visible on cmd.exe
inspect.styles = {
    'special': 'cyan',
    'number': 'yellow',
    'boolean': 'yellow',
    'undefined': 'grey',
    'null': 'bold',
    'string': 'green',
    'symbol': 'green',
    'date': 'magenta',
    // "name": intentionally not styling
    'regexp': 'red'
};


var formatRegExp = /%[sdj%]/g;
exports.format = function (f) {
    if (!isString(f)) {
        var objects = [];
        for (var j = 0; j < arguments.length; j++) {
            objects.push(inspect(arguments[j]));
        }
        return objects.join(' ');
    }

    var i = 1;
    var args = arguments;
    var len = args.length;
    var str = String(f).replace(formatRegExp, function (x) {
        if (x === '%%') return '%';
        if (i >= len) return x;
        switch (x) {
            case '%s':
                return String(args[i++]);
            case '%d':
                return Number(args[i++]);
            case '%j':
                try {
                    return JSON.stringify(args[i++]);
                } catch (_) {
                    return '[Circular]';
                }
            default:
                return x;
        }
    });
    for (var x = args[i]; i < len; x = args[++i]) {
        if (isNull(x) || !isObject(x)) {
            str += ' ' + x;
        } else {
            str += ' ' + inspect(x);
        }
    }
    return str;
};

Harvested from: https://github.com/joyent/node/blob/master/lib/util.js

Central answered 4/3, 2009 at 12:53 Comment(0)
F
5

Just in case someone needs a function to prevent polluting global scope, here is the function that does the same:

  function _format (str, arr) {
    return str.replace(/{(\d+)}/g, function (match, number) {
      return typeof arr[number] != 'undefined' ? arr[number] : match;
    });
  };
Fuddle answered 4/3, 2009 at 12:53 Comment(0)
R
4

I have a slightly longer formatter for JavaScript here...

You can do formatting several ways:

  • String.format(input, args0, arg1, ...)
  • String.format(input, obj)
  • "literal".format(arg0, arg1, ...)
  • "literal".format(obj)

Also, if you have say a ObjectBase.prototype.format (such as with DateJS) it will use that.

Examples...

var input = "numbered args ({0}-{1}-{2}-{3})";
console.log(String.format(input, "first", 2, new Date()));
//Outputs "numbered args (first-2-Thu May 31 2012...Time)-{3})"

console.log(input.format("first", 2, new Date()));
//Outputs "numbered args(first-2-Thu May 31 2012...Time)-{3})"

console.log(input.format(
    "object properties ({first}-{second}-{third:yyyy-MM-dd}-{fourth})"
    ,{
        'first':'first'
        ,'second':2
        ,'third':new Date() //assumes Date.prototype.format method
    }
));
//Outputs "object properties (first-2-2012-05-31-{3})"

I've also aliased with .asFormat and have some detection in place in case there's already a string.format (such as with MS Ajax Toolkit (I hate that library).

Repeater answered 4/3, 2009 at 12:53 Comment(0)
L
3

another suggestion is you use the string template:

const getPathDadosCidades = (id: string) =>  `/clientes/${id}`

const getPathDadosCidades = (id: string, role: string) =>  `/clientes/${id}/roles/${role}`
Lamebrain answered 4/3, 2009 at 12:53 Comment(1)
thanks, the idea of putting this into a lambda just saved me a lot of faff!Monotheism
M
3

I didn't see pyformat in the list so I thought I'd throw it in:

console.log(pyformat( 'The {} {} jumped over the {}'
                , ['brown' ,'fox' ,'foobar']
                ))
console.log(pyformat('The {0} {1} jumped over the {1}'
                , ['brown' ,'fox' ,'foobar']
                ))
console.log(pyformat('The {color} {animal} jumped over the {thing}'
                , [] ,{color: 'brown' ,animal: 'fox' ,thing: 'foobaz'}
                ))
Muddleheaded answered 4/3, 2009 at 12:53 Comment(0)
L
2
/**
 * Format string by replacing placeholders with value from element with
 * corresponsing index in `replacementArray`.
 * Replaces are made simultaneously, so that replacement values like
 * '{1}' will not mess up the function.
 *
 * Example 1:
 * ('{2} {1} {0}', ['three', 'two' ,'one']) -> 'one two three'
 *
 * Example 2:
 * ('{0}{1}', ['{1}', '{0}']) -> '{1}{0}'
 */
function stringFormat(formatString, replacementArray) {
    return formatString.replace(
        /\{(\d+)\}/g, // Matches placeholders, e.g. '{1}'
        function formatStringReplacer(match, placeholderIndex) {
            // Convert String to Number
            placeholderIndex = Number(placeholderIndex);

            // Make sure that index is within replacement array bounds
            if (placeholderIndex < 0 ||
                placeholderIndex > replacementArray.length - 1
            ) {
                return placeholderIndex;
            }

            // Replace placeholder with value from replacement array
            return replacementArray[placeholderIndex];
        }
    );
}
Lundquist answered 4/3, 2009 at 12:53 Comment(0)
C
2

With sprintf.js in place - one can make a nifty little format-thingy

String.prototype.format = function(){
    var _args = arguments 
    Array.prototype.unshift.apply(_args,[this])
    return sprintf.apply(undefined,_args)
}   
// this gives you:
"{%1$s}{%2$s}".format("1", "0")
// {1}{0}
Cyprian answered 4/3, 2009 at 12:53 Comment(0)
C
2

For use with jQuery.ajax() success functions. Pass only a single argument and string replace with the properties of that object as {propertyName}:

String.prototype.format = function () {
    var formatted = this;
    for (var prop in arguments[0]) {
        var regexp = new RegExp('\\{' + prop + '\\}', 'gi');
        formatted = formatted.replace(regexp, arguments[0][prop]);
    }
    return formatted;
};

Example:

var userInfo = ("Email: {Email} - Phone: {Phone}").format({ Email: "[email protected]", Phone: "123-123-1234" });
Cod answered 4/3, 2009 at 12:53 Comment(0)
S
2

I did not see the String.format variant:

String.format = function (string) {
    var args = Array.prototype.slice.call(arguments, 1, arguments.length);
    return string.replace(/{(\d+)}/g, function (match, number) {
        return typeof args[number] != "undefined" ? args[number] : match;
    });
};
Slaw answered 4/3, 2009 at 12:53 Comment(0)
B
1

Here is a very short function that does a subset of printf and shows the result in the developer console:

function L(...values)
    {
    // Replace each '@', starting with the text in the first arg
    console.log(values.reduce(function(str,arg) {return str.replace(/@/,arg)}));
    } // L

Here is a test:

let a=[1,2,3];
L('a: [@]',a);

Output is similar to: a=[1,2,3]

Byington answered 4/3, 2009 at 12:53 Comment(0)
O
1

I use the template literal approach, like below:

export const messages = {
  foo: (arg1, arg2) => `Hello ${arg1} ${arg2}`,
  bar: (arg1) => `Hello ${arg1}`,
}

From the file:

console.log(messages.foo('Bar', 'World'))
console.log(messages.bar('Foo'))
Oxytocic answered 4/3, 2009 at 12:53 Comment(0)
C
1

String.prototype.format = function(){
    var final = String(this);
    for(let i=0; i<arguments.length;i++){
        final = final.replace(`%s${i+1}`, arguments[i])
    }
    return final || ''
}

console.log(("hello %s2 how %s3 you %s1").format('hi', 'hello', 'how'));
<h1 id="text">
   
</h1>
Consecration answered 4/3, 2009 at 12:53 Comment(0)
D
1

I needed a function which could format a price (given in cents) in a way preferred by the user, and the tricky part is that the format is specified by the user -- and I do not expect my users to understand printf-like syntax, or regexps, etc. My solution is somewhat similar to that used in Basic, so the user just marks with # places for digits, for example:

simple_format(1234567,"$ ###,###,###.##")
"$ 12,345.67"
simple_format(1234567,"### ### ###,## pln")
"12 345,67 pln"

I believe this is quite easy to understand by user, and quite easy to implement:

function simple_format(integer,format){
  var text = "";
  for(var i=format.length;i--;){
    if(format[i]=='#'){
      text = (integer%10) + text;
      integer=Math.floor(integer/10);
      if(integer==0){
        return format.substr(0,i).replace(/#(.*#)?/,"")+text;
      }
    }else{
      text = format[i] + text;
    }
  }
  return text;
}
Dvorak answered 4/3, 2009 at 12:53 Comment(0)
A
1

There is also Globalize.format in the jQuery Globalize project, the official globalization service for jQuery UI. IT's nice when you need culture-aware formatting.

Assemblage answered 4/3, 2009 at 12:53 Comment(0)
T
1

arg function:

/**
 * Qt stil arg()
 * var scr = "<div id='%1' class='%2'></div>".arg("mydiv").arg("mydivClass");
 */
String.prototype.arg = function() {
    var signIndex = this.indexOf("%");
    var result = this;
    if (signIndex > -1 && arguments.length > 0) {
        var argNumber = this.charAt(signIndex + 1);
        var _arg = "%"+argNumber;
        var argCount = this.split(_arg);
        for (var itemIndex = 0; itemIndex < argCount.length; itemIndex++) {
            result = result.replace(_arg, arguments[0]);
        }
    }
    return result;
}
Traipse answered 4/3, 2009 at 12:53 Comment(0)
H
0

I am answering this question because of the following reasons.

  • sprintf/printf allows you to provide variable lengths of arguments.
  • The top answers are using template literals. When using template literals you must know the number of arguments you have.

@Braden Best and @David Spector's answers seem valid from my perspective.

I am adding the following answer so that someone can find the answer in one place.

Explanation:

  • In the method, you're passing your desired template string in the first parameter, having placeholder :param. Alongside that, you can pass as many replacers as you want.
  • It then iterates over the values passed and replaces the next :param with the currently iterating value.

Mainly, if you know what Array.reduce and String.replace do, you understand the code.

You can change the :param to anything you want. Also, you will need to change the :param within the sprintf method in that case.

function sprintf(format, ...values) {
  return values.reduce((carry, current) => carry.replace(/:param/, current), format);
}

console.log(sprintf('Hello :param! How are you :param. Are you over :param?', 'World', 'Anik', 18));
console.log(sprintf('Nothing to be replaced in here!'));
console.log(sprintf('https://httpbin.org/users/:param/posts/:param', 'anik', 'abcdefgh'));
console.log(sprintf('hello :param! How are you :param. Are you over :param? ":param"', 'world', 'anik', 18, ['extra', 'params']));
console.log(sprintf('hello :param'));
console.log(sprintf('hello', 1,2,3,4));
Homerus answered 4/3, 2009 at 12:53 Comment(0)
F
0

I needed a step more forward solution.

A template I could reuse to generate strings not only in declaration but in a random time in the execution time.

So I came across with this jig:

 class Texplate{
    constructor(...args){
        this.data = args;
    }

    apply(...args){
        var text = "";
        var i = 0, j = 0, n = this.data.length, m = args.length;
        for(;i < n && j < m; i++, j++){
            text += this.data[i] + args[j];
        }

        for (; i < n; i++){
            text += this.data[i];
        }

        for (; j < m; j++){
            text += args[j];
        }

        return text;        
    }
}

This allow to create a Text template which works internally as the array merge algorithm, starting with the text array defined in constructor.

An example of use:

var Textplate example = new Texplate("Hello, ", "!"); 
console.log(example.apply("Frank"));
console.log(example.apply("Mary"));
console.log(example.apply());
console.log(example.apply("Frank", " Have a good day!"));
Fortuitism answered 4/3, 2009 at 12:53 Comment(0)
D
0

Modified code of old answer https://mcmap.net/q/10141/-javascript-equivalent-to-printf-string-format much more efficient (without slow RegExp) and shorter

String.prototype.formatUnicorn = function () {
    let str = this.toString();
    if(!arguments.length) {
        return;
    };
    const [args] = arguments;
    for (const key of Object.keys(args)) {
        str = str.replaceAll(`{${key}}`, args[key]);
    };
    return str;
};

usage:

"{test} {test_2} {test}".formatUnicorn({"test": "hello", "test_2": "world"}); // yields hello world hello

benchmark between new and old: https://jsben.ch/BRovx

Dogie answered 4/3, 2009 at 12:53 Comment(0)
A
0

Ok, so first we'll set up some variables to use:

    const date = new Date();
    
    const locale = 'en-us';
    
    const wDay   = date.toLocaleString(locale, {weekday: 'short'});
    const month  = date.toLocaleString(locale, {month: 'long'});
    const year   = date.toLocaleString(locale, {year: 'numeric'});
    const minute = date.toLocaleString(locale, {minute: 'numeric'});
    const [hour, ap] = date.toLocaleString(locale, {hour: 'numeric', hour12:true}).split(' ');
    
    let mDay = date.toLocaleString(locale, {day: 'numeric'});
    
    switch(mDay % 10)
    {
        case 1:  mDay += 'st'; break;
        case 2:  mDay += 'nd'; break;
        case 3:  mDay += 'rd'; break;
        default: mDay += 'th'; break;
    }

Now that we've got all that, we can format a string like so:

    const formatter = (...a) => `${a[0]}, the ${a[1]} of ${a[2]} ${a[3]} at ${a[4]}:${a[5]} ${a[6]}`;
    const formatted = formatter(wDay, mDay, month, year, hour, minute, ap);

We could even use named paramaters for the "formatter" function:

    const formatter = (wDay, mDay, month, year, hour, minute, ap) => `${wDay}, the ${mDay} of ${month} ${year} at ${hour}:${minute} ${ap}`;
    const formatted = formatter(wDay, mDay, month, year, hour, minute, ap);

If you'll notice, the JS templates above are both the results of callbacks. If the entire piece of code above were encapsulated within a function that was expected to return a formatted date, it would not be hard to imagine how to construct an arbitrary "formatter" function in the same manner, that could be passed in from outside.

tl;dr you can re-use template literals if you put them inside callbacks and use the args as the replacements.

Arin answered 4/3, 2009 at 12:53 Comment(0)
T
0

Right now, there is a package called locutus which translate the functions of other languages to Javascript such as php, python, ruby etc.

const printf = require('locutus/php/strings/printf')
printf('Hello world');

You can try this playground codesandbox

Thimbleweed answered 4/3, 2009 at 12:53 Comment(1)
I found a bug and contributed a fix. It should work perfectly now. Thank you for mentioning!Edmonds
C
0

If you need a printf, use printf

Looks like 90% of commenters never used printf with more complex format than just %d. I wonder how do they output, for example, money values?

Christiniachristis answered 4/3, 2009 at 12:53 Comment(1)
Formatting outside the string. (213.3244).toFixed(2) exists in JavaScript, other languages have other methods (C# has Decimal.Truncate(), or you use Math.Round()/Math.round()). Cultural information is also present in many languages. System.Globalization for C# is an example, (324).toLocaleString() in JavaScript. This answer also only works in Node.js, not the browser. Considering the question doesn't specify if its browser or node, this answer could be considered rather useless to the situation (tho helpful to others coming here). Template strings are the best solution here.Mirandamire
D
0

if you just need to format a string with %s specifier only

function _sprintf(message){
    const regexp = RegExp('%s','g');
    let match;
    let index = 1;
    while((match = regexp.exec(message)) !== null) {
        let replacement = arguments[index];
        if (replacement) {
            let messageToArray = message.split('');
            messageToArray.splice(match.index, regexp.lastIndex - match.index, replacement);
            message = messageToArray.join('');
            index++;
        } else {
            break;
        }
    }

    return message;
}

_sprintf("my name is %s, my age is %s", "bob", 50); // my name is bob, my age is 50
Depersonalization answered 4/3, 2009 at 12:53 Comment(0)
H
0

In typescript create a file named format.ts and import it whatever you need to use formatting.

// contents of format.ts

interface String {
  format(...args: any[]): string;
}

if (!String.prototype.format) {
  String.prototype.format = function() {
    let a = this;
    let b: any;
    // tslint:disable-next-line: forin
    for (b in arguments) {
      a = a.replace(/%[a-z]/, arguments[b]);
    }
    return a;
  };
}

To format string use this code:

import './format';

console.log('Hello, %s!'.format('World'));

Example

String.prototype.format = function() {
  let a = this;
  let b;
  for (b in arguments) {
    a = a.replace(/%[a-z]/, arguments[b]);
  }
  return a;
};

console.log('Hello, %s!'.format('World'));
Hued answered 4/3, 2009 at 12:53 Comment(0)
S
0
export function stringFormat (str: string, ...args: string[]) {
     return args.reduce((acc, curr, i) => acc.replace(new RegExp("\\{" + i + "\\}", 'g'), curr), str);
}
Stockbroker answered 4/3, 2009 at 12:53 Comment(0)
S
0

I started porting the Java String.format (actually new Formatter().format()) to javascript. The initial version is available at:

https://github.com/RobAu/javascript.string.format

You can simple add the javscript and call StringFormat.format("%.2f", [2.4]); etc.

Please note it is NOT finished yet, but feedback is welcome :)

Secretary answered 4/3, 2009 at 12:53 Comment(2)
So why add it as a possible solution?Tarbox
"My basic requirement is a thousand separator format for numbers for now" - it handles that just fineSecretary
J
0

This is an implementation of https://mcmap.net/q/10141/-javascript-equivalent-to-printf-string-format for CoffeeScript.

https://gist.github.com/eces/5669361

if String.prototype.format is undefined
  String.prototype.format = () ->
    _arguments = arguments
    this.replace /{(\d+)}/g, (match, number) ->
      if typeof _arguments[number] isnt 'undefined' then _arguments[number] else match
Juniejunieta answered 4/3, 2009 at 12:53 Comment(0)
P
0
String.prototype.repeat = function(n) { 
    return new Array(++n).join(this); 
};

String.prototype.pad = function(requiredLength, paddingStr, paddingType) {    
    var n = requiredLength - this.length; 

    if (n) {
        paddingType = paddingType ? paddingType.toLowerCase() : '';
        paddingStr = paddingStr || ' ';
        paddingStr = paddingStr.repeat( Math.ceil(n / paddingStr.length) ).substr(0, n);

        if (paddingType == 'both') {
            n /= 2;
            return paddingStr.substr( 0, Math.ceil(n) ) + this + paddingStr.substr( 0, Math.floor(n) );
        }   

        if (paddingType == 'left') {
            return paddingStr + this;
        }

        return this + paddingStr;
    } 

    return this; 
}; 

// синтаксис аналогичен printf
// 'Привет, %s!'.format('мир') -> "Привет, мир!"
// '%.1s.%.1s. %s'.format('Иван', 'Иванович', 'Иванов') -> "И.И. Иванов"
String.prototype.format = function() {
    var i = 0, 
        params = arguments;

    return this.replace(/%(?:%|(?:(|[+-]+)(|0|'.+?)([1-9]\d*)?(?:\.([1-9]\d*))?)?(s|d|f))/g, function(match, sign, padding, width, precision, type) {
        if (match == '%%') { 
            return '%'; 
        }

        var v = params[i++];

        if (type == 'd') { 
            v = Math.round(v); 
        }
        else if (type == 'f') {
            v = v.toFixed(precision ? precision : 6);
        }

        if (/\+/.test(sign) && v > 0) {
            v = '+' + v;
        }

        v += '';

        if (type != 'f' && precision) {
            v = v.substr(0, precision);
        }

        if (width) {
            v = v.pad(width, padding == '' ? ' ' : padding[0] == "'" ? padding.substr(1) : padding, /-/.test(sign) ? 'right' : 'left'); 
        }

        return v;
    });
};

// this.name = 'Вася';
// console.log( 'Привет, ${name}!'.template(this) );
// "Привет, Вася!"
String.prototype.template = function(context) {
    return this.replace(/\$\{(.*?)\}/g, function(match, name) {
        return context[name];
    });
};
Petropavlovsk answered 4/3, 2009 at 12:53 Comment(0)
L
0

bobjs can do this:

var sFormat = "My name is {0} and I am {1} years old."; 
var result = bob.string.formatString(sFormat, "Bob", 29); 
console.log(result); 
//output: 
//========== 
// My name is Bob and I am 29 years old. 
Lemire answered 4/3, 2009 at 12:53 Comment(0)
M
0

You can use this function

            String.prototype.format = function (args) {
            var str = this;
            return str.replace(String.prototype.format.regex, function(item) {
                var intVal = parseInt(item.substring(1, item.length - 1));
                var replace;
                if (intVal >= 0) {
                    replace = args[intVal];
                } else if (intVal === -1) {
                    replace = "{";
                } else if (intVal === -2) {
                    replace = "}";
                } else {
                    replace = "";
                }
                return replace;
            });
        };
        String.prototype.format.regex = new RegExp("{-?[0-9]+}", "g");

        // Sample usage.
        var str = "She {1} {0}{2} by the {0}{3}. {-1}^_^{-2}";
        str = str.format(["sea", "sells", "shells", "shore"]);
        alert(str);
Morph answered 4/3, 2009 at 12:53 Comment(1)
Using parseInt() as @Morph has here yields something surprisingly competitive, performancewise… I benched both a tidied-up version of this idea and a regex-caching variant of same here: jsperf.com/stringformat/6#results – the parseInt() implementations come out at or near the top (vs. a bunch of the other versions of String.format() harvested from snippets posted by this q’s answerers).Leekgreen
S
0

This one works with {0}, {1} and {}.

String.prototype.format = function format()
{                                                                                                               
  var msg = this;
  for(var i in arguments)
    msg = msg.replace(/\{\}/,arguments[i]).replace(new RegExp('\\{'+i+'\\}','g'),arguments[i]);
  return msg;
}
Slovenly answered 4/3, 2009 at 12:53 Comment(0)
A
-1

Just use template strings and arrow functions. This even makes them reusable.

const greeter = (name, birthplace) => `Hi, I'm ${name}, I'm from ${birthplace}!`;


console.log( greeter('Sean', 'Earth') )

If you use the ...spread operator you can use positioned params:

const greeterPosition = (...a) => `Hi, I'm ${a[0]}, I'm from ${a[1]}!`;

console.log( greeterPosition('Sean', 'Earth') );

You can even compose them if you like:

const greeterA = (name) => `Hi, I'm ${name}`;
const greeterB = (birthplace) => `I'm from ${birthplace}`;

const greeterComposed = (...a) => greeterA(a[0]) + ', ' + greeterB(a[1]) + '!';

console.log( greeterComposed('Sean', 'Earth') );

If you REALLY need type restrictions:

// alias the types for conciseness' sake
const [b,n,s,h] = [Boolean,Number,String,d=>Number(d).toString(16)];

const onlyBools   = (...a) => `onlyBools:\nThese were Bools ${b(a[0])} ${b(a[1])}.\nThese were strings: ${b(a[2])}, ${b(a[3])}.\n...And this was a number: ${b(a[4])}`;
const onlyNumbers = (...a) => `onlyNumbers:\nThese were Bools ${n(a[0])} ${n(a[1])}.\nThese were strings: ${n(a[2])}, ${n(a[3])}.\n...And this was a number: ${n(a[4])}`;
const onlyHex     = (...a) => `onlyHex:\nThese were Bools ${h(a[0])} ${h(a[1])}.\nThese were strings: ${h(a[2])}, ${h(a[3])}.\n...And this was a number: ${h(a[4])}`;
const onlyStrings = (...a) => `onlyStrings:\nThese were Bools ${s(a[0])} ${s(a[1])}.\nThese were strings: ${s(a[2])}, ${s(a[3])}.\n...And this was a number: ${s(a[4])}`;


console.log( onlyBools(true, false, 'not boolean', '', 77) );
console.log( onlyNumbers(true, false, 'not boolean', '', 77) );
console.log( onlyHex(true, false, 'not boolean', '', 77) );
console.log( onlyStrings(true, false, 'not boolean', '', 77) );
Arin answered 4/3, 2009 at 12:53 Comment(0)
D
-1

sprintf() function analog in JavaScript as Vue filter and String.prototype.format() extension:

/**
 * Returns a formatted string.
 *
 * @param template
 * @param values
 * @return string
 */
String.format = function (template, ...values) {
    let i = -1;

    function callback(exp, p0, p1, p2, p3, p4) {
        if (exp === '%%') return '%';
        if (values[++i] === undefined) return undefined;

        exp = p2 ? parseInt(p2.substr(1)) : undefined;

        let base = p3 ? parseInt(p3.substr(1)) : undefined;
        let val;

        switch (p4) {
            case 's': val = values[i]; break;
            case 'c': val = values[i][0]; break;
            case 'f': val = parseFloat(values[i]).toFixed(exp); break;
            case 'p': val = parseFloat(values[i]).toPrecision(exp); break;
            case 'e': val = parseFloat(values[i]).toExponential(exp); break;
            case 'x': val = parseInt(values[i]).toString(base ? base : 16); break;
            case 'd': val = parseFloat(parseInt(values[i], base ? base : 10).toPrecision(exp)).toFixed(0); break;
        }
        val = typeof (val) == 'object' ? JSON.stringify(val) : val.toString(base);
        let sz = parseInt(p1); /* padding size */
        let ch = p1 && p1[0] === '0' ? '0' : ' '; /* isnull? */

        while (val.length < sz) val = p0 !== undefined ? val + ch : ch + val; /* isminus? */

        return val;
    }

    let regex = /%(-)?(0?[0-9]+)?([.][0-9]+)?([#][0-9]+)?([scfpexd%])/g;

    return template.replace(regex, callback);
}

String.prototype.format = function() {
    return String.format(this, ...arguments);
}

const StringFormat = {
    install: (Vue, options) => {
        Vue.filter('format', function () {
            return String.format(...arguments);
        });
    },
};

export default StringFormat;

Original answer: JavaScript equivalent to printf/String.Format

Decagram answered 4/3, 2009 at 12:53 Comment(0)
S
-1

Not the most recommended function in the world, but it works.

If you need sprintf, just copy & paste this same function and change return console.log(sb) to just return sb.

printf = function(s, /*args...*/) {
    a = arguments;
    al = a.length;
    
    if (al <= 1) return -2;
    if (al >= 2 && s.toLowerCase().search(/%[a-z]/) == -1) return -1;

    sb = s;
    for (i = 1; i <= al - 1; i++) {
        sb = sb.replace(/%[a-z]/, a[i]);
    }

    return console.log(sb);
}

var someString = "Hello %s\nIt's %s:%s %s now.\nThe day is %s\n";
printf(someString, "StackOverflowUser", "5", "48", "PM", "beautiful");
Schreiber answered 4/3, 2009 at 12:53 Comment(0)
S
-1

This is not an exact duplicate of sprintf; however, it is similar and more powerful: https://github.com/anywhichway/stringformatter

Format expressions using this library take the form of embedded Javascript objects, e.g.

format("I have {number: {currency: "$", precision:2}}.",50.2); 

will return "I have $50.20.".

Staciestack answered 4/3, 2009 at 12:53 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.