JavaScript equivalent of Python's format() function?
Asked Answered
M

20

82

Python has this beautiful function to turn this:

bar1 = 'foobar'
bar2 = 'jumped'
bar3 = 'dog'

foo = 'The lazy ' + bar3 + ' ' + bar2 ' over the ' + bar1
# The lazy dog jumped over the foobar

Into this:

bar1 = 'foobar'
bar2 = 'jumped'
bar3 = 'dog'

foo = 'The lazy {} {} over the {}'.format(bar3, bar2, bar1)
# The lazy dog jumped over the foobar

Does JavaScript have such a function? If not, how would I create one which follows the same syntax as Python's implementation?

Mcdade answered 11/2, 2011 at 21:26 Comment(9)
Look at this thread for a solution: #610906Stacked
I always use jQuery in many of my sites, so it wouldn't hurt. Does JS allow you to subclass or add functions to string objects?Mcdade
JavaScript is a prototype-based language.. You can extend all objects of a type by enhancing their common prototype.Heartache
Okay. So String.prototype has nothing to do with Prototype.js? I always though that's what the prototype means...Mcdade
prototype is a feature of the language. The prototypejs library is entirely separate.Maurita
possible duplicate of Javascript equivalent to python's .format()Futrell
@Stefano: That question was asked almost two years later and has less answers.Mcdade
@Mcdade I know, and I have nothing against your question :-) The first answer on the other one seem the best to me. In any case, it would be nice to have one of the two marked as duplicate as they totally are... whichever one!Futrell
This isn't too broad at all. If you are familiar with python's string.format method then you know exactly what the user is asking.Nervous
A
69

Another approach, using the String.prototype.replace method, with a "replacer" function as second argument:

String.prototype.format = function () {
  var i = 0, args = arguments;
  return this.replace(/{}/g, function () {
    return typeof args[i] != 'undefined' ? args[i++] : '';
  });
};

var bar1 = 'foobar',
    bar2 = 'jumped',
    bar3 = 'dog';

'The lazy {} {} over the {}'.format(bar3, bar2, bar1);
// "The lazy dog jumped over the foobar"
Acriflavine answered 11/2, 2011 at 22:25 Comment(6)
Yep, makes sense. jsfiddle.net/wFb2p/6 I hadn't previously grasped the fact that the function runs once for each match. +1Maurita
Using a function for replacement also means you can pretty easily extend this to support non=empty placeholders such as {1}.Armpit
Extending native classes is discouraged. You should rather use a function, or define your own class / subclass. This helps other programmers distinguish native and non-native features. It also avoids the risk of code interference -- someone else defines might define String.Prototype.format too.Porter
Why isn't this a standard part of JavaScript?Pact
I use this function on every JS project, thank you!Farra
Why is this one not the most famous answer in SO? +1Quillet
I
65

There is a way, but not exactly using format.

var name = "John";
var age = 19;
var message = `My name is ${name} and I am ${age} years old`;
console.log(message);

jsfiddle - link

Insensate answered 6/7, 2015 at 14:38 Comment(8)
How does this work? I am not familiar with the ${...} in Javascript. Awesome though!Globe
Its compatible with ES6Insensate
This is all pretty interesting but that doesn't solve the problem. The method from ES6 is pointless in many case if we cannot pass it a particular context. What you wrote is mostly just syntactic sugar for string concatenation.Gasconade
This does solve the problem and if you don't have context, you can easily do things like var message = `My name is ${name || 'default'} and I am ${age || 'default'} years old`;Appreciate
This solves the problem very well. This would be similar to the python code 'My name is {name} and I am {age}'.format(**locals())Nervous
developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…Nervous
@shrewmouse: Or just f'My name is {name} and I am {age}' in Python 3.6.Mcdade
@Nervous thanks, this document is very important to me. I used ${name} but it didin't work. And I read the document you referred, it says "Template literals are enclosed by the backtick (` `) (grave accent) character instead of double or single quotes." So I realize what mistake I have made.Omegaomelet
C
36

tl;dr

foo = (a, b, c) => `The lazy ${a} ${b} over the ${c}`

Why template strings alone aren't enough

ES6 template strings provide a feature quite similar to pythons string format. However, you have to know the variables before you construct the string:

var templateString = `The lazy ${bar3} ${bar2} over the ${bar1}`;

Why format?

Python's str.format allows you to specify the string before you even know which values you want to plug into it, like:

foo = 'The lazy {} {} over the {}'

bar1 = 'foobar'
bar2 = 'jumped'
bar3 = 'dog'

foo.format(bar3, bar2, bar1)

Solution

With an arrow function, we can elegantly wrap the template string for later use:

foo = (a, b, c) => `The lazy ${a} ${b} over the ${c}`

bar1 = 'foobar';
bar2 = 'jumped';
bar3 = 'dog';

foo(bar3, bar2, bar1)

Of course this works with a regular function as well, but the arrow function allows us to make this a one-liner. Both features are available in most browsers und runtimes:

Calpac answered 22/10, 2018 at 17:19 Comment(3)
This misses a major reason for using xx.format(), with the same drawbacks as @Yash Mehrotra's answer, which is that the string to be formatted might need to be dynamic rather than hardcoded.Unprincipled
This should be the top answer. Thank you!Pyrogenous
@Unprincipled You can just swap out foo, no? e.g. if (some_condition) { fmt = foo } else { fmt = bar } then fmt(bar3, bar2, bar1)Temuco
T
12

Looking for an answer for the same question, I just found this: https://github.com/davidchambers/string-format, which is "JavaScript string formatting inspired by Python’s str.format()". It seem it's pretty much the same as python's format() function.

Trommel answered 23/11, 2013 at 12:37 Comment(2)
What I was looking for. It doesn't support Python padding feature for the moment though.Porter
It doesn't support the format specifiers mini-language, and it doesn't look like it ever will. Work on that branch died a long time ago. (See github.com/davidchambers/string-format/tree/mini-language)Ulterior
D
7

Taken from YAHOOs library:

YAHOO.Tools.printf = function() { 
  var num = arguments.length; 
  var oStr = arguments[0];   
  for (var i = 1; i < num; i++) { 
    var pattern = "\\{" + (i-1) + "\\}"; 
    var re = new RegExp(pattern, "g"); 
    oStr = oStr.replace(re, arguments[i]); 
  } 
  return oStr; 
} 

Call it like:

bar1 = 'foobar'
bar2 = 'jumped'
bar3 = 'dog'

foo = YAHOO.Tools.printf('The lazy {0} {1} over the {2}', bar3, bar2, bar1); 
Danikadanila answered 11/2, 2011 at 21:31 Comment(0)
W
6

You can use template literals in JS,

const bar1 = 'foobar'
const bar2 = 'jumped'
const bar3 = 'dog'
foo = `The lazy ${bar3} ${bar2} over the ${bar1}`

I think this was helpful.

Wattmeter answered 16/12, 2019 at 7:56 Comment(0)
M
4

Here's my first attempt. Feel free to point out flaws.

Example: http://jsfiddle.net/wFb2p/5/

String.prototype.format = function() {
    var str = this;
    var i = 0;
    var len = arguments.length;
    var matches = str.match(/{}/g);
    if( !matches || matches.length !== len ) {
        throw "wrong number of arguments";
    }
    while( i < len ) {
        str = str.replace(/{}/, arguments[i] );
        i++;
    }
    return str;
};

EDIT: Made it a bit more efficient by eliminating the .match() call in the while statement.

EDIT: Changed it so that the same error is thrown if you don't pass any arguments.

Maurita answered 11/2, 2011 at 21:31 Comment(3)
Whoa! That was fast. Thank you!Mcdade
make sure you check that arguments[i] exists.Heartache
@zzzzBov: Good point. I was just about to give an update that throws an error if the number of arguments doesn't match the number of {} found. EDIT: Updated.Maurita
W
2

Usando split:

String.prototype.format = function (args) {
    var text = this
    for(var attr in args){
        text = text.split('${' + attr + '}').join(args[attr]);
    }
    return text
};

json = {'who':'Gendry', 'what':'will sit', 'where':'in the Iron Throne'}
text = 'GOT: ${who} ${what} ${where}';

console.log('context: ',json);
console.log('template: ',text);
console.log('formated: ',text.format(json));

Usando Regex:

String.prototype.format = function (args) {
    var text = this
    for(var attr in args){
        var rgx = new RegExp('\\${' + attr + '}','g');
        text = text.replace(rgx, args[attr]);
    }
    return text
};

json = {'who':'Gendry', 'what':'will sit', 'where':'in the Iron Throne'}
text = 'GOT: ${who} ${what} ${where}';

console.log('context: ',json);
console.log('template: ',text);
console.log('formated: ',text.format(json));
Weirick answered 14/5, 2019 at 15:37 Comment(1)
Handy. I implemented this outside of the prototype, and added a hasOwnProperty check: const format = (s, args) => { for (let attr in args) if (args.hasOwnProperty(attr)) s = s.split('${' + attr + '}').join(args[attr]); return s || ''; };Suttle
S
1

This code allows you to specify exactly which brackets to replace with which strings. The brackets don't need to be in the same order as the arguments, and multiple brackets are possible. The format function takes an array of values as its parameter, with each key being one of the bracketed 'variables' which is replaced by its corresponding value.

String.prototype.format = function (arguments) {
    var this_string = '';
    for (var char_pos = 0; char_pos < this.length; char_pos++) {
        this_string = this_string + this[char_pos];
    }

    for (var key in arguments) {
        var string_key = '{' + key + '}'
        this_string = this_string.replace(new RegExp(string_key, 'g'), arguments[key]);
    }
    return this_string;
};

'The time is {time} and today is {day}, {day}, {day}. Oh, and did I mention that the time is {time}.'.format({day:'Monday',time:'2:13'});
//'The time is 2:13 and today is Monday, Monday, Monday. Oh, and did I mention that the time is 2:13.'
Semipermeable answered 15/8, 2014 at 13:47 Comment(1)
Seems good except the first part of the code is kind of pointless as String are technically immutable. You don't really have to concatenate each letters. Simply this_string = this. Should be enough. As you're going to use replace later, the this will not be touched anyway.Gasconade
D
1

JS:

String.prototype.format = function () {
    var str = this;
    for (var i = 0; i < arguments.length; i++) {
        str = str.replace('{' + i + '}', arguments[i]);
    }
    return str;
}

bar1 = 'foobar';
bar2 = 'jumped';
bar3 = 'dog';

python_format = 'The lazy {2} {1} over the {0}'.format(bar1,bar2,bar3);

document.getElementById("demo").innerHTML = "JavaScript equivalent of Python's format() function:<br><span id='python_str'>" + python_format + "</span>";

HTML:

<p id="demo"></p>

CSS:

span#python_str {
    color: red;
    font-style: italic;
}

OUTPUT:

JavaScript equivalent of Python's format() function:

The lazy dog jumped over the foobar

DEMO:

jsFiddle

Dalmatia answered 23/2, 2015 at 8:3 Comment(0)
P
1
String.prototype.format = function () {
    var i=0,args=arguments,formats={
        "f":(v,s,c,f)=>{s=s||' ',c=parseInt(c||'0'),f=parseInt(f||'-1');v=f>0?Math.floor(v).toString()+"."+Math.ceil(v*Math.pow(10,f)).toString().slice(-f):(f==-1?v.toString():Math.floor(v).toString());return c>v.length?s.repeat(c-v.length)+v:v;},
        "d":(v,s,c,f)=>{s=s||' ',c=parseInt(c||'0');v=Math.floor(v).toString();return c>v.length?s.repeat(c-v.length)+v:v;},
        "s":(v,s,c,f)=>{s=s||' ',c=parseInt(c||'0');return c>v.length?s.repeat(c-v.length)+v:v;},
        "x":(v,s,c,f)=>{s=s||' ',c=parseInt(c||'0');v=Math.floor(v).toString(16);return c>v.length?s.repeat(c-v.length)+v:v;},
        "X":(v,s,c,f)=>{s=s||' ',c=parseInt(c||'0');v=Math.floor(v).toString(16).toUpperCase();return c>v.length?s.repeat(c-v.length)+v:v;},
    };
    return this.replace(/{(\d+)?:?([0=-_*])?(\d+)?\.?(\d+)?([dfsxX])}/g, function () {
        let pos = arguments[1]||i;i++;
        return typeof args[pos] != 'undefined' ? formats[arguments[5]](args[pos],arguments[2],arguments[3],arguments[4]) : '';
    });
};
Precocious answered 16/12, 2019 at 7:52 Comment(0)
S
1

Simple implementation without using extra function

[bar1, bar2, bar3].reduce(
  (str, val) => str.replace(/{}/, val),
  'The lazy {} {} over the {}'
)
Sync answered 13/5, 2021 at 18:18 Comment(0)
B
1
String.prototype.format = function(args){
  //args - hash of params
  var text = this
  for(var attr in args){
    text = text.split('${' + attr + '}').join(args[attr]);
  }
  return text
}

Usage: 'Hello, ${NAME}. This is ${FORMAT}'.format({NAME:'guys',FORMAT:'format()'})

UPDATE: Python's f-terminated strings:

Python: f'string {expr}'
JavaScript: `string ${expr}`
Buncombe answered 7/11, 2022 at 10:25 Comment(0)
P
0

JavaScript doesn't have such a function AFAIK.

You could create one by modifying the String class's prototype object to add a format() method which takes a variable number of arguments.

In the format method you'd have to get the String's instance value (the actual string) and then parse it for '{}' and insert the appropriate argument.

Then return the new string to the caller.

Pentimento answered 11/2, 2011 at 21:31 Comment(0)
H
0

JavaScript does not have a string formatting function by default, although you can create your own or use one someone else has made (such as sprintf)

Heartache answered 11/2, 2011 at 21:34 Comment(0)
O
0

In the file

https://github.com/BruceSherwood/glowscript/blob/master/lib/glow/api_misc.js

is a function String.prototype.format = function(args) that fully implements the Python string.format() function, not limited simply to handling character strings.

Orelia answered 24/6, 2015 at 4:55 Comment(1)
Not fully. It doesn't support nested expressions like "start {:.{}f} end".format(14, 5)Exhortative
P
0

For those looking for a simple ES6 solution.

First of all I'm providing a function instead of extending native String prototype because it is generally discouraged.

// format function using replace() and recursion

const format = (str, arr) => arr.length > 1 
	? format(str.replace('{}', arr[0]), arr.slice(1)) 
	: (arr[0] && str.replace('{}', arr[0])) || str

// Example usage

const str1 = 'The {} brown {} jumps over the {} dog'

const formattedString = formatFn(str1, ['quick','fox','lazy'])

console.log(formattedString)
Phyl answered 29/1, 2019 at 7:27 Comment(0)
U
0

If you (like me) only need the limited subset of python's format function for simple string replacement, and performance is not critical, a very simple 29-line pure-Javascript function may suffice.

Javascript call: format(str, data)

Analogous python call: str.format(**data), with the caveat that this javascript function, unlike Python's, does not throw an error if the string contains a varname that is not found in the provided data.

/*
 * format(str, data): analogous to Python's str.format(**data)
 *
 * Example:
 *   let data = {
 *     user: {
 *       name: { first: 'Jane', last: 'Doe' }
 *     },
 *     email: '[email protected]',
 *     groups: ["one","two"]
 *   };
 *
 *   let str = 'Hi {user.name.first} {user.name.last}, your email address is {email}, and your second group is {groups[1]}'
 * 
 *   format(str, data)
 *   => returns "Hi Jane Doe, your email address is [email protected], and your second group is two"
 */

function format(str, data) {
    var varnames = {};
    function array_path(path, i) {
        var this_k = '[' + i + ']';
        if (!path.length)
            return [this_k];
        path = path.slice();
        path[path.length - 1] += this_k;
        return path;
    }
    function add_varnames(this_data, path) {
        if (this_data.constructor == Array) {
            for (var i = 0; i < this_data.length; i++)
                add_varnames(this_data[i], array_path(path, i));
        }
        else if (this_data.constructor == Object) {
            for (var k in this_data)
                add_varnames(this_data[k], path.concat(k));
        }
        else {
            var varname = '{' + path.join('.') + '}';
            varnames[varname] = String(this_data);
        }
    }
    add_varnames(data, []);
    for (var varname in varnames)
        str = str.replace(varname, varnames[varname]);
    return str;
}
Unprincipled answered 25/3, 2020 at 17:22 Comment(0)
E
0

My own srtipped down version of YAHOO's printf reported by PatrikAkerstrand:

function format() { 
  return [...arguments].reduce((acc, arg, idx) => 
    acc.replace(new RegExp("\\{" + (idx - 1) + "\\}", "g"), arg));
}

console.log(
  format('Confirm {1} want {0} beers', 3, 'you')
);
Educationist answered 16/4, 2020 at 8:23 Comment(0)
R
0

That method support index-based and position-based insertion both

Link to the source code

const formatStr = (str: string, ...args: (string | number | boolean)[]) => {
  let i = -1;
  return str.replace(/{\d}|{}/g, (match) => {
    i += 1;
    if (match === "{}") {
        return typeof args[i] !== "undefined" ? String(args[i]) : "";
    }
    const idx = parseInt(match.replace(/{|}/g, ""));
    return typeof args[idx] !== "undefined" ? String(args[idx]) : "";
  });
};

...

formatStr("hello {1} world {0} foo {}", 1,2,3) // hello 2 world 1 foo 3

Rajkot answered 15/3 at 9:47 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.