Chris S
Chris S

Reputation: 65456

JavaScript equivalent to printf/String.Format

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.

Upvotes: 2460

Views: 2498804

Answers (30)

ssi-anik
ssi-anik

Reputation: 3714

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));

Upvotes: 1

Kim
Kim

Reputation: 1497

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.

Upvotes: 320

Sean Morris
Sean Morris

Reputation: 77

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) );

Upvotes: -1

Francisco M
Francisco M

Reputation: 303

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!"));

Upvotes: 0

David Spector
David Spector

Reputation: 1677

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]

Upvotes: 1

Gumbo
Gumbo

Reputation: 655697

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.

Upvotes: 1645

Mahendra Hirapra
Mahendra Hirapra

Reputation: 379

export function stringFormat (str: string, ...args: string[]) {
     return args.reduce((acc, curr, i) => acc.replace(new RegExp("\\{" + i + "\\}", 'g'), curr), str);
}

Upvotes: 0

Mkoes
Mkoes

Reputation: 70

Modified code of old answer https://stackoverflow.com/a/18234317/19531844 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

Upvotes: 1

Satish Chandra Gupta
Satish Chandra Gupta

Reputation: 3351

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));

Upvotes: 35

Jens T&#246;rnell
Jens T&#246;rnell

Reputation: 24788

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'))

Upvotes: 1

George Eracleous
George Eracleous

Reputation: 4422

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

util.format("%s world", "Hello")

Upvotes: 133

Sean Morris
Sean Morris

Reputation: 472

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.

Upvotes: 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

Upvotes: 0

Alex Povolotsky
Alex Povolotsky

Reputation: 402

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?

Upvotes: 0

Gabriel Nahmias
Gabriel Nahmias

Reputation: 978

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.

Upvotes: 670

GHosT
GHosT

Reputation: 276

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

Upvotes: -1

Nero
Nero

Reputation: 1577

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

Upvotes: 0

user7090116
user7090116

Reputation:

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");

Upvotes: -1

Lucas Breitembach
Lucas Breitembach

Reputation: 1683

another suggestion is you use the string template:

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

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

Upvotes: 3

TheMisir
TheMisir

Reputation: 4279

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'));

Upvotes: 0

rescdsk
rescdsk

Reputation: 8915

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(), ...)

Upvotes: 363

Murtaza Hussain
Murtaza Hussain

Reputation: 4315

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

Upvotes: 6

NISHANK KUMAR
NISHANK KUMAR

Reputation: 525

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>

Upvotes: 1

Braden Best
Braden Best

Reputation: 8998

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! %%"

Upvotes: 28

Rtlprmft
Rtlprmft

Reputation: 19

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'

Upvotes: 28

Yevgeniy Afanasyev
Yevgeniy Afanasyev

Reputation: 41430

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!

Upvotes: 5

Monarch Wadia
Monarch Wadia

Reputation: 4996

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")
);

Upvotes: 69

Chris S
Chris S

Reputation: 65456

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.

Upvotes: 14

jsxt
jsxt

Reputation: 1135

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());

Upvotes: 32

fearphage
fearphage

Reputation: 16948

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}

Upvotes: 1510

Related Questions