Equivalent of String.format in jQuery
Asked Answered
A

22

205

I'm trying to move some JavaScript code from MicrosoftAjax to JQuery. I use the JavaScript equivalents in MicrosoftAjax of the popular .net methods, e.g. String.format(), String.startsWith(), etc. Are there equivalents to them in jQuery?

Anemometry answered 24/6, 2009 at 14:30 Comment(1)
see #610906Fluted
G
194

The source code for ASP.NET AJAX is available for your reference, so you can pick through it and include the parts you want to continue using into a separate JS file. Or, you can port them to jQuery.

Here is the format function...

String.format = function() {
  var s = arguments[0];
  for (var i = 0; i < arguments.length - 1; i++) {       
    var reg = new RegExp("\\{" + i + "\\}", "gm");             
    s = s.replace(reg, arguments[i + 1]);
  }

  return s;
}

And here are the endsWith and startsWith prototype functions...

String.prototype.endsWith = function (suffix) {
  return (this.substr(this.length - suffix.length) === suffix);
}

String.prototype.startsWith = function(prefix) {
  return (this.substr(0, prefix.length) === prefix);
}
Graubert answered 24/6, 2009 at 15:3 Comment(12)
Doesn't look like there's much to it. The JavaScript version doesn't have all the fancy number formatting stuff, obviously. blog.stevex.net/index.php/string-formatting-in-csharpSofta
Wow, I actually already thought about this but also thought it was not possible because of the license, didn't know they released it under Microsoft Permissive License, thanks a lot for thisAnemometry
License or no license.. there's only one right way to write something so simpleNigger
Constructing (and then discarding) a RegEx object for each and every argument each time format gets called might overtax the garbage collector.Crossbench
@Crossbench You can alternatively use substring if you have those kinds of concerns, but I certainly wouldn't be too worried about that; usually when you are formatting a string you have three of these, at best.Graubert
I see, this is just useful as indexing. It does not really support the formatting just like what C# can do...Debbee
Warning: This will format recursively: so if you have {0}{1}, {0} will be replaced first, and then all occurrences of {1} in both the already-substituted text and the original format will be replaced.Inhambane
How can I port them to jQuery ?Swampy
@Inhambane alternative code without format recursively ?Swampy
@Swampy Read the other answers; at least one addresses it. Please don't overuse formatting.Inhambane
Seems like the code for this is in MicrosoftAjax.js#L515Prohibition
This throws ESLint error ˇString prototype is read only, properties should not be addedˇ How to fix?Contraception
N
155

This is a faster/simpler (and prototypical) variation of the function that Josh posted:

String.prototype.format = String.prototype.f = function() {
    var s = this,
        i = arguments.length;

    while (i--) {
        s = s.replace(new RegExp('\\{' + i + '\\}', 'gm'), arguments[i]);
    }
    return s;
};

Usage:

'Added {0} by {1} to your collection'.f(title, artist)
'Your balance is {0} USD'.f(77.7) 

I use this so much that I aliased it to just f, but you can also use the more verbose format. e.g. 'Hello {0}!'.format(name)

Nigger answered 15/4, 2010 at 19:43 Comment(2)
With modern browsers there are even more simple approaches: https://mcmap.net/q/10176/-equivalent-of-string-format-in-jquerySlobbery
@Slobbery Template string are NOT the same thing at all really. They are syntactic sugar for string concatenation, which is nice but not the same as formatting. They run instantly, therefore, the replacements must be in scope at the point of definition. You cannot store them in a text file or database because of this. In fact, the only way to store them anywhere at all is to put them in a function. A formatted string function takes positional replacements that do not care about the name of the variable.Afflatus
G
133

Many of the above functions (except Julian Jelfs's) contain the following error:

js> '{0} {0} {1} {2}'.format(3.14, 'a{2}bc', 'foo');
3.14 3.14 afoobc foo

Or, for the variants that count backwards from the end of the argument list:

js> '{0} {0} {1} {2}'.format(3.14, 'a{0}bc', 'foo');
3.14 3.14 a3.14bc foo

Here's a correct function. It's a prototypal variant of Julian Jelfs's code, which I made a bit tighter:

String.prototype.format = function () {
  var args = arguments;
  return this.replace(/\{(\d+)\}/g, function (m, n) { return args[n]; });
};

And here is a slightly more advanced version of the same, which allows you to escape braces by doubling them:

String.prototype.format = function () {
  var args = arguments;
  return this.replace(/\{\{|\}\}|\{(\d+)\}/g, function (m, n) {
    if (m == "{{") { return "{"; }
    if (m == "}}") { return "}"; }
    return args[n];
  });
};

This works correctly:

js> '{0} {{0}} {{{0}}} {1} {2}'.format(3.14, 'a{2}bc', 'foo');
3.14 {0} {3.14} a{2}bc foo

Here is another good implementation by Blair Mitchelmore, with a bunch of nice extra features: https://web.archive.org/web/20120315214858/http://blairmitchelmore.com/javascript/string.format

Geneviegenevieve answered 22/2, 2011 at 10:46 Comment(4)
And another one, which I haven't looked at too closely, but which seems to implement formats like {0:+$#,0.00;-$#,0.00;0}: masterdata.dyndns.org/r/string_format_for_javascriptGeneviegenevieve
Ooh, and one that uses the Python interpolation format: code.google.com/p/jquery-utils/wiki/…Geneviegenevieve
The format implementation I mentioned two comments up has moved to masterdata.se/r/string_format_for_javascriptGeneviegenevieve
Note: ianj's answer elsewhere on this page enables you to use named instead of numeric parameters. If you change his method into one that uses prototypes, you will have to change the second parameter to the slice.call function from 1 to 0.Geneviegenevieve
S
50

Made a format function that takes either a collection or an array as arguments

Usage:

format("i can speak {language} since i was {age}",{language:'javascript',age:10});

format("i can speak {0} since i was {1}",'javascript',10});

Code:

var format = function (str, col) {
    col = typeof col === 'object' ? col : Array.prototype.slice.call(arguments, 1);

    return str.replace(/\{\{|\}\}|\{(\w+)\}/g, function (m, n) {
        if (m == "{{") { return "{"; }
        if (m == "}}") { return "}"; }
        return col[n];
    });
};
Sealed answered 17/3, 2011 at 16:11 Comment(4)
Nice one, all that is missing is: String.prototype.format = function (col) {return format(this,col);}Baggywrinkle
i prefer not to extend stringSealed
there is a small typo in the usage : 2nd line should be : format("i can speak {0} since i was {1}",'javascript',10);Future
Why not prefer extend string using String.prototype ?Swampy
I
36

There is an (somewhat) official option: jQuery.validator.format.

Comes with jQuery Validation Plugin 1.6 (at least).
Quite similar to the String.Format found in .NET.

Edit Fixed broken link.

Isogamy answered 9/2, 2010 at 19:22 Comment(0)
T
17

If you're using the validation plugin you can use:

jQuery.validator.format("{0} {1}", "cool", "formatting") = 'cool formatting'

http://docs.jquery.com/Plugins/Validation/jQuery.validator.format#templateargumentargumentN...

Teddytedeschi answered 19/8, 2011 at 19:48 Comment(1)
JQuery minimal version ?Swampy
L
13

Though not exactly what the Q was asking for, I've built one that is similar but uses named placeholders instead of numbered. I personally prefer having named arguments and just send in an object as an argument to it (more verbose, but easier to maintain).

String.prototype.format = function (args) {
    var newStr = this;
    for (var key in args) {
        newStr = newStr.replace('{' + key + '}', args[key]);
    }
    return newStr;
}

Here's an example usage...

alert("Hello {name}".format({ name: 'World' }));
Ludhiana answered 21/6, 2011 at 3:29 Comment(0)
S
12

Using a modern browser, which supports EcmaScript 2015 (ES6), you can enjoy Template Strings. Instead of formatting, you can directly inject the variable value into it:

var name = "Waleed";
var message = `Hello ${name}!`;

Note the template string has to be written using back-ticks (`).

Slobbery answered 9/12, 2016 at 4:12 Comment(0)
S
6

None of the answers presented so far has no obvious optimization of using enclosure to initialize once and store regular expressions, for subsequent usages.

// DBJ.ORG string.format function
// usage:   "{0} means 'zero'".format("nula") 
// returns: "nula means 'zero'"
// place holders must be in a range 0-99.
// if no argument given for the placeholder, 
// no replacement will be done, so
// "oops {99}".format("!")
// returns the input
// same placeholders will be all replaced 
// with the same argument :
// "oops {0}{0}".format("!","?")
// returns "oops !!"
//
if ("function" != typeof "".format) 
// add format() if one does not exist already
  String.prototype.format = (function() {
    var rx1 = /\{(\d|\d\d)\}/g, rx2 = /\d+/ ;
    return function() {
        var args = arguments;
        return this.replace(rx1, function($0) {
            var idx = 1 * $0.match(rx2)[0];
            return args[idx] !== undefined ? args[idx] : (args[idx] === "" ? "" : $0);
        });
    }
}());

alert("{0},{0},{{0}}!".format("{X}"));

Also, none of the examples respects format() implementation if one already exists.

Significative answered 7/8, 2011 at 1:3 Comment(2)
rx2 is unnecessary, see my implementation; you have (parentheses) in rx1, but don't use the value they pass to the inner function. Also, I think this is an obvious optimization to do in the Javascript engine. Are you sure modern browsers don't already do this optimization behind the scenes? Perl did so in 1990. You are right that there should be a wrapper around the function to check if it is already implemented.Geneviegenevieve
Also, the test on (args[idx] === "") looks superfluous to me: it is already covered by the first test on that line, because undefined !== "".Geneviegenevieve
O
6

Way past the late season but I've just been looking at the answers given and have my tuppence worth:

Usage:

var one = strFormat('"{0}" is not {1}', 'aalert', 'defined');
var two = strFormat('{0} {0} {1} {2}', 3.14, 'a{2}bc', 'foo');

Method:

function strFormat() {
    var args = Array.prototype.slice.call(arguments, 1);
    return arguments[0].replace(/\{(\d+)\}/g, function (match, index) {
        return args[index];
    });
}

Result:

"aalert" is not defined
3.14 3.14 a{2}bc foo
Occam answered 18/10, 2017 at 1:28 Comment(0)
C
4

Here's mine:

String.format = function(tokenised){
        var args = arguments;
        return tokenised.replace(/{[0-9]}/g, function(matched){
            matched = matched.replace(/[{}]/g, "");
            return args[parseInt(matched)+1];             
        });
    }

Not bullet proof but works if you use it sensibly.

Carreon answered 4/2, 2011 at 9:55 Comment(0)
D
3

Here's my version that is able to escape '{', and clean up those unassigned place holders.

function getStringFormatPlaceHolderRegEx(placeHolderIndex) {
    return new RegExp('({)?\\{' + placeHolderIndex + '\\}(?!})', 'gm')
}

function cleanStringFormatResult(txt) {
    if (txt == null) return "";

    return txt.replace(getStringFormatPlaceHolderRegEx("\\d+"), "");
}

String.prototype.format = function () {
    var txt = this.toString();
    for (var i = 0; i < arguments.length; i++) {
        var exp = getStringFormatPlaceHolderRegEx(i);
        txt = txt.replace(exp, (arguments[i] == null ? "" : arguments[i]));
    }
    return cleanStringFormatResult(txt);
}
String.format = function () {
    var s = arguments[0];
    if (s == null) return "";

    for (var i = 0; i < arguments.length - 1; i++) {
        var reg = getStringFormatPlaceHolderRegEx(i);
        s = s.replace(reg, (arguments[i + 1] == null ? "" : arguments[i + 1]));
    }
    return cleanStringFormatResult(s);
}
Deth answered 23/2, 2011 at 19:22 Comment(0)
D
2

The following answer is probably the most efficient but has the caveat of only being suitable for 1 to 1 mappings of arguments. This uses the fastest way of concatenating strings (similar to a stringbuilder: array of strings, joined). This is my own code. Probably needs a better separator though.

String.format = function(str, args)
{
    var t = str.split('~');
    var sb = [t[0]];
    for(var i = 0; i < args.length; i++){
        sb.push(args[i]);
        sb.push(t[i+1]);
    }
    return sb.join("");
}

Use it like:

alert(String.format("<a href='~'>~</a>", ["one", "two"]));
Derrickderriey answered 3/1, 2014 at 19:5 Comment(3)
Accepted answer is the best answer. I'm giving a unique answer that is useful in scenarios where you want to squeeze every bit of efficiency possible (long loops) and you have 1:1 mapping of args. More efficient cuz replace() and new Regex() along with performing the regex definitely uses more CPU cycles than single split().Derrickderriey
No need for -1. No, it's not widely applicable, but he's correct, this would be slightly more efficient. Now, to the author, an architecture question: what kind of application would be dealing with such a large dataset on the client that this sort of optimization would be required?Anserine
Good point Michael Blackburn. Most situations are probably fine. In my case I was dealing with a good chunk of data (product variations within a product group, so possibly hundreds of variants) and my opinion is that since users typically have many browser tabs open and every website tends to suck a lot of CPU that I preferred this implementation to maintain high efficiency.Derrickderriey
S
2

This violates DRY principle, but it's a concise solution:

var button = '<a href="{link}" class="btn">{text}</a>';
button = button.replace('{text}','Authorize on GitHub').replace('{link}', authorizeUrl);
Shoemake answered 19/8, 2015 at 1:58 Comment(0)
P
2

Now you can use Template Literals:

var w = "the Word";
var num1 = 2;
var num2 = 3;

var long_multiline_string = `This is very long
multiline templete string. Putting somthing here:
${w}
I can even use expresion interpolation:
Two add three = ${num1 + num2}
or use Tagged template literals
You need to enclose string with the back-tick (\` \`)`;

console.log(long_multiline_string);
Panamerican answered 24/7, 2016 at 23:38 Comment(1)
I was excited about template literals until I saw that they only work when the string is defined alongside the replacement variables. To me that makes them pretty much useless; for one reason or another most of my strings are defined separately from the code that populates/uses them.Yonder
A
0

I couldn't get Josh Stodola's answer to work, but the following worked for me. Note the specification of prototype. (Tested on IE, FF, Chrome, and Safari.):

String.prototype.format = function() {
    var s = this;
    if(t.length - 1 != args.length){
        alert("String.format(): Incorrect number of arguments");
    }
    for (var i = 0; i < arguments.length; i++) {       
        var reg = new RegExp("\\{" + i + "\\}", "gm");
        s = s.replace(reg, arguments[i]);
    }
    return s;
}

s really should be a clone of this so as not to be a destructive method, but it's not really necessary.

Andrea answered 11/2, 2011 at 20:5 Comment(1)
It's not a destructive method. When s gets reassigned with the return value of s.replace(), this remains untouched.Geneviegenevieve
M
0
<html>
<body>
<script type="text/javascript">
   var str="http://xyz.html?ID={0}&TId={1}&STId={2}&RId={3},14,480,3,38";
   document.write(FormatString(str));
   function FormatString(str) {
      var args = str.split(',');
      for (var i = 0; i < args.length; i++) {
         var reg = new RegExp("\\{" + i + "\\}", "");             
         args[0]=args[0].replace(reg, args [i+1]);
      }
      return args[0];
   }
</script>
</body>
</html>
Monocular answered 28/3, 2012 at 6:25 Comment(1)
This is fine for URIs, but for general usage, having one string contain both the format and the components is very brittle. What if your string contains commas?Anserine
C
0

Expanding on adamJLev's great answer above, here is the TypeScript version:

// Extending String prototype
interface String {
    format(...params: any[]): string;
}

// Variable number of params, mimicking C# params keyword
// params type is set to any so consumer can pass number
// or string, might be a better way to constraint types to
// string and number only using generic?
String.prototype.format = function (...params: any[]) {
    var s = this,
        i = params.length;

    while (i--) {
        s = s.replace(new RegExp('\\{' + i + '\\}', 'gm'), params[i]);
    }

    return s;
};
Creolized answered 24/4, 2015 at 3:2 Comment(0)
O
0

I have a plunker that adds it to the string prototype: string.format It is not just as short as some of the other examples, but a lot more flexible.

Usage is similar to c# version:

var str2 = "Meet you on {0}, ask for {1}";
var result2 = str2.format("Friday", "Suzy"); 
//result: Meet you on Friday, ask for Suzy
//NB: also accepts an array

Also, added support for using names & object properties

var str1 = "Meet you on {day}, ask for {Person}";
var result1 = str1.format({day: "Thursday", person: "Frank"}); 
//result: Meet you on Thursday, ask for Frank
Octagonal answered 14/9, 2015 at 15:30 Comment(1)
you should clean the unused placeholders. var result1 = str1.format({day: "Thursday"}); Meet you on Thursday, ask for {Person}Biak
M
0

You can also closure array with replacements like this.

var url = '/getElement/_/_/_'.replace(/_/g, (_ => this.ar[this.i++]).bind({ar: ["invoice", "id", 1337],i: 0}))
> '/getElement/invoice/id/1337

or you can try bind

'/getElement/_/_/_'.replace(/_/g, (function(_) {return this.ar[this.i++];}).bind({ar: ["invoice", "id", 1337],i: 0}))
Muskogee answered 10/6, 2016 at 2:55 Comment(0)
J
0
// Regex cache
_stringFormatRegex = null;
//
/// Formats values from {0} to {9}. Usage: stringFormat( 'Hello {0}', 'World' );
stringFormat = function () {
    if (!_stringFormatRegex) {
        // Initialize
        _stringFormatRegex = [];
        for (var i = 0; i < 10; i++) {
            _stringFormatRegex[i] = new RegExp("\\{" + i + "\\}", "gm");
        }
    }
    if (arguments) {
        var s = arguments[0];
        if (s) {
            var L = arguments.length;
            if (1 < L) {
                var r = _stringFormatRegex;
                for (var i = 0; i < L - 1; i++) {
                    var reg = r[i];
                    s = s.replace(reg, arguments[i + 1]);
                }
            }
        }
        return s;
    }
}
Jamilla answered 13/8, 2021 at 12:2 Comment(0)
S
0

Using functional programming:

// 'Hello, {0} {1}'.format('FirstName', 'LastName') -> 'Hello, FirstName LastName'
String.prototype.format = function () {
  const initialValue = this.toString();
  const numberOfArguments = arguments.length || 0;
  const formattedValue = [...Array(numberOfArguments)].reduce((accumulator, currentValue, index) => {
    const replacementPattern = new RegExp('\\{' + index + '\\}', 'gm');
    const updatedValued = accumulator.replace(replacementPattern, arguments[index]);

    return updatedValued;
  }, initialValue);

  return formattedValue;
};
Succubus answered 10/2, 2023 at 18:46 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.