78

This is driving me nuts. I believe I asked this exact same question, but I can't find it any more (I used Stack Overflow search, Google Search, manually searched my posts, and searched my code).

I wanted something that would be like the C# String.Format where you could do something like

string format = String.Format("Hi {0}",name);

just for JavaScript of course and one person gave me a simple answer it was not like a jQuery plugin or anything, but I think you made some JSON thing or something, and it worked and was simple to use.

I for the life of me can't find this post.

I do have this in my code, but I can't seem to find anything that uses it and I am pretty sure I used it a couple of times:

String.prototype.format = function(o)
{
    return this.replace(/{([^{}]*)}/g,
       function(a, b)
       {
           var r = o[b];
           return typeof r === 'string' ? r : a;
       }
    );
};
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
chobo2
  • 83,322
  • 195
  • 530
  • 832

19 Answers19

74

Adapt the code from MsAjax string.

Just remove all of the _validateParams code and you are most of the way to a full fledged .NET string class in JavaScript.

Okay, I liberated the msajax string class, removing all the msajax dependencies. It Works great, just like the .NET string class, including trim functions, endsWith/startsWith, etc.

P.S. - I left all of the Visual Studio JavaScript IntelliSense helpers and XmlDocs in place. They are innocuous if you don't use Visual Studio, but you can remove them if you like.

<script src="script/string.js" type="text/javascript"></script>
<script type="text/javascript">
    var a = String.format("Hello {0}!", "world");
    alert(a);

</script>

String.js

// String.js - liberated from MicrosoftAjax.js on 03/28/10 by Sky Sanders
// permalink: http://stackoverflow.com/a/2534834/2343

/*
    Copyright (c) 2009, CodePlex Foundation
    All rights reserved.

    Redistribution and use in source and binary forms, with or without modification, are permitted
    provided that the following conditions are met:

    *   Redistributions of source code must retain the above copyright notice, this list of conditions
        and the following disclaimer.

    *   Redistributions in binary form must reproduce the above copyright notice, this list of conditions
        and the following disclaimer in the documentation and/or other materials provided with the distribution.

    *   Neither the name of CodePlex Foundation nor the names of its contributors may be used to endorse or
        promote products derived from this software without specific prior written permission.

    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY EXPRESS OR IMPLIED
    WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
    A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
    FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
    LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
    INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
    OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
    IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.</textarea>
*/

(function(window) {

    $type = String;
    $type.__typeName = 'String';
    $type.__class = true;

    $prototype = $type.prototype;
    $prototype.endsWith = function String$endsWith(suffix) {
        /// <summary>Determines whether the end of this instance matches the specified string.</summary>
        /// <param name="suffix" type="String">A string to compare to.</param>
        /// <returns type="Boolean">true if suffix matches the end of this instance; otherwise, false.</returns>
        return (this.substr(this.length - suffix.length) === suffix);
    }

    $prototype.startsWith = function String$startsWith(prefix) {
        /// <summary >Determines whether the beginning of this instance matches the specified string.</summary>
        /// <param name="prefix" type="String">The String to compare.</param>
        /// <returns type="Boolean">true if prefix matches the beginning of this string; otherwise, false.</returns>
        return (this.substr(0, prefix.length) === prefix);
    }

    $prototype.trim = function String$trim() {
        /// <summary >Removes all leading and trailing white-space characters from the current String object.</summary>
        /// <returns type="String">The string that remains after all white-space characters are removed from the start and end of the current String object.</returns>
        return this.replace(/^\s+|\s+$/g, '');
    }

    $prototype.trimEnd = function String$trimEnd() {
        /// <summary >Removes all trailing white spaces from the current String object.</summary>
        /// <returns type="String">The string that remains after all white-space characters are removed from the end of the current String object.</returns>
        return this.replace(/\s+$/, '');
    }

    $prototype.trimStart = function String$trimStart() {
        /// <summary >Removes all leading white spaces from the current String object.</summary>
        /// <returns type="String">The string that remains after all white-space characters are removed from the start of the current String object.</returns>
        return this.replace(/^\s+/, '');
    }

    $type.format = function String$format(format, args) {
        /// <summary>Replaces the format items in a specified String with the text equivalents of the values of   corresponding object instances. The invariant culture will be used to format dates and numbers.</summary>
        /// <param name="format" type="String">A format string.</param>
        /// <param name="args" parameterArray="true" mayBeNull="true">The objects to format.</param>
        /// <returns type="String">A copy of format in which the format items have been replaced by the   string equivalent of the corresponding instances of object arguments.</returns>
        return String._toFormattedString(false, arguments);
    }

    $type._toFormattedString = function String$_toFormattedString(useLocale, args) {
        var result = '';
        var format = args[0];

        for (var i = 0; ; ) {
            // Find the next opening or closing brace
            var open = format.indexOf('{', i);
            var close = format.indexOf('}', i);
            if ((open < 0) && (close < 0)) {
                // Not found: copy the end of the string and break
                result += format.slice(i);
                break;
            }
            if ((close > 0) && ((close < open) || (open < 0))) {

                if (format.charAt(close + 1) !== '}') {
                    throw new Error('format stringFormatBraceMismatch');
                }

                result += format.slice(i, close + 1);
                i = close + 2;
                continue;
            }

            // Copy the string before the brace
            result += format.slice(i, open);
            i = open + 1;

            // Check for double braces (which display as one and are not arguments)
            if (format.charAt(i) === '{') {
                result += '{';
                i++;
                continue;
            }

            if (close < 0) throw new Error('format stringFormatBraceMismatch');


            // Find the closing brace

            // Get the string between the braces, and split it around the ':' (if any)
            var brace = format.substring(i, close);
            var colonIndex = brace.indexOf(':');
            var argNumber = parseInt((colonIndex < 0) ? brace : brace.substring(0, colonIndex), 10) + 1;

            if (isNaN(argNumber)) throw new Error('format stringFormatInvalid');

            var argFormat = (colonIndex < 0) ? '' : brace.substring(colonIndex + 1);

            var arg = args[argNumber];
            if (typeof (arg) === "undefined" || arg === null) {
                arg = '';
            }

            // If it has a toFormattedString method, call it.  Otherwise, call toString()
            if (arg.toFormattedString) {
                result += arg.toFormattedString(argFormat);
            }
            else if (useLocale && arg.localeFormat) {
                result += arg.localeFormat(argFormat);
            }
            else if (arg.format) {
                result += arg.format(argFormat);
            }
            else
                result += arg.toString();

            i = close + 1;
        }

        return result;
    }

})(window);
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Sky Sanders
  • 36,396
  • 8
  • 69
  • 90
  • 2
    I didn't know that it was possible for Javascript to look like C ... :-D. Very nice work Sky, thanks for taking the time to comment it! – Sean Vieira Mar 28 '10 at 22:47
  • 2
    @Sean, I didn't write the code or comments, I just took the string class from msajax and removed/replaced all of the external dependencies. This gives you a lot of very useful and familiar string functions from .net. I figure if you are going to do something, do it right. Trivial, brittle string munging snippets are a recipe for a headache. – Sky Sanders Mar 28 '10 at 22:53
  • Hmm I will check this out in a couple mins. Thanks but do you have to make everything a string? Like could I have a variable where "World" is like the String.Format takes in an object. Also is there a limit of how many place holders you can send in. I know String.Format after 3 place holders you got to send in an array in. – chobo2 Mar 28 '10 at 22:59
  • @chobo2 - no, just as in .net, every object in js has a .toString function. Pass anything you want. Unlike .net, the arguments object makes it easy to accept any number of arguments, so just keep adding as many as you want. `String.format("{0},....",true,"aString",new Date(),["a","b"]);` make sense? – Sky Sanders Mar 28 '10 at 23:49
  • All but the last one. would that be an array so it would fill up place holder {3} & {4}? – chobo2 Mar 29 '10 at 00:10
  • @chobo2 - no, just an illustration that you can pass any object as a format argument. This js String.format implementation accepts only an unlimited number of arguments. You could create an alternative that accepts only an array. – Sky Sanders Mar 29 '10 at 00:29
  • 1
    Can I suggest embedding the URL of your answer http://stackoverflow.com/a/2534834/21061 into the script? It's useful to be able to go back to the source – Ian Jul 15 '14 at 12:52
  • Thanks, useful code. But this implementation fails to address the cases where an array is passed to the `format` function as the second argument. To fix it, I changed the `_toFormattedString` function to take `(useLocale, format, args)` and changed the line where its called, removed the `var format = ...` line, and removed the `+1` from the end of the `var argNumber = ...` line. May I suggest you reflect this in your answer for the next users? :) – Iravanchi May 18 '15 at 07:01
  • Link to source now points to an empty CodePlex repo - they've moved to [GitHub](https://github.com/DevExpress/AjaxControlToolkit/blob/6c99014ea33808636a9f8c3c579e99bc6121bd75/AjaxControlToolkit.Jasmine/Scripts/MicrosoftAjax.js#L527) – ry8806 Jul 27 '16 at 08:43
45

Here is what I use. I have this function defined in a utility file:

  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 I call it like so:

var greeting = String.format("Hi, {0}", name);

I do not recall where I found this, but it has been very useful to me. I like it because the syntax is the same as the C# version.

Jeremy
  • 9,307
  • 3
  • 21
  • 17
20

You can do series of replaces like that:

function format(str)
{
    for(i = 1; i < arguments.length; i++)
    {
        str = str.replace('{' + (i - 1) + '}', arguments[i]);
    }
    return str;
}

Better approach will be to use replace with function parameter:

function format(str, obj) {
    return str.replace(/\{\s*([^}\s]+)\s*\}/g, function(m, p1, offset, string) {
        return obj[p1]
    })
}

This way you can provide both indices and named parameters:

var arr = ['0000', '1111', '2222']

arr.a = 'aaaa'

str = format(" { 0 } , {1}, { 2}, {a}", arr)
// returns 0000 , 1111, 2222, aaaa
vittore
  • 17,449
  • 6
  • 44
  • 82
  • @Ismail: take a look at history of changes it was reformated with wrong quot char – vittore Mar 09 '11 at 19:40
  • 1
    +1 Thanks. Works great now. Check it here on [JsFiddle](http://jsfiddle.net/ismusidhu/9Jpkv/) – IsmailS Mar 10 '11 at 06:22
  • @vittore The second method is very cool but I cannot get it working. it finds the correct strings to use to retrieve properties but is not replacing them (at lease not as intended) e.g.: http://jsfiddle.net/9Jpkv/24/ –  Jun 16 '15 at 08:34
  • 1
    @gordatron you are not passing params as an object. http://jsfiddle.net/9Jpkv/27/ – vittore Jun 16 '15 at 13:48
19

Without a third party function:

string format = "Hi {0}".replace('{0}', name)

With multiple params:

string format = "Hi {0} {1}".replace('{0}', name).replace('{1}', lastname)
Dustin Laine
  • 37,935
  • 10
  • 86
  • 125
  • Hmm well look at what I posted as I am not sure what is exactly does but it is using replace. Like I said I remember it used json or maybe and array of stuff. So it might be just a more elaborate way of what your doing. – chobo2 Mar 28 '10 at 22:36
  • 1
    Yes, I am not sure what post you are looking for, this is a simple alternative using built in JS functions. – Dustin Laine Mar 28 '10 at 22:46
  • 5
    `"Hi {0} {1}. {0}. ".replace('{0}', "John").replace('{1}', "Smith");` returns `"Hi John Smith. {0}."` – IsmailS Sep 20 '10 at 05:19
14

Here is a useful string formatting function using regular expressions and captures:

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

Strings can be formatted like C# String.Format:

var str = format('{0}, {1}!', 'Hello', 'world');
console.log(str); // prints "Hello, world!"

the format will place the correct variable in the correct spot, even if they appear out of order:

var str = format('{1}, {0}!', 'Hello', 'world');
console.log(str); // prints "world, Hello!"

Hope this helps!

pje
  • 2,458
  • 1
  • 25
  • 26
4

Use a template literal in ECMAScript 6:

var customer = { name: "Foo" }
var card = { amount: 7, product: "Bar", unitprice: 42 }
var message = `Hello ${customer.name},
               want to buy ${card.amount} ${card.product} for
               a total of ${card.amount * card.unitprice} bucks?`
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
gavin
  • 1,276
  • 1
  • 11
  • 17
3

String.Format method from .NET Framework has multiple signatures. The one I like the most uses params keyword in its prototype, i.e.:

public static string Format(
    string format,
    params Object[] args
)

Using this version, you can not only pass variable number of arguments to it but also an array argument.

Because I like the straightforward solution provided by Jeremy, I'd like to extend it a bit:

var StringHelpers = {
    format: function(format, args) {
        var i;
        if (args instanceof Array) {
            for (i = 0; i < args.length; i++) {
                format = format.replace(new RegExp('\\{' + i + '\\}', 'gm'), args[i]);
            }
            return format;
        }
        for (i = 0; i < arguments.length - 1; i++) {
            format = format.replace(new RegExp('\\{' + i + '\\}', 'gm'), arguments[i + 1]);
        }
        return format;
    }
};

Now you can use your JavaScript version of String.Format in the following manners:

StringHelpers.format("{0}{1}", "a", "b")

and

StringHelpers.format("{0}{1}", ["a", "b"])
jwaliszko
  • 16,942
  • 22
  • 92
  • 158
3

Just make and use this function:

function format(str, args) {
   for (i = 0; i < args.length; i++)
      str = str.replace("{" + i + "}", args[i]);
   return str;
}

If you don't want to change the str parameter, then before the for loop, clone (duplicate) it to a new string (make a new copy of str), and set the copy in the for loop and at last return it instead of the parameter itself.

In C# (Sharp) it is simple create to a copy by just calling String.Clone(), but I don't know how in JavaScript, but you can search on Google or surf on the Internet and learn ways to do it.

I just gave you my idea about string format in JavaScript.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
2

Based on @roydukkey's answer, a bit more optimized for runtime (it caches the regexes):

(function () {
    if (!String.prototype.format) {
        var regexes = {};
        String.prototype.format = function (parameters) {
            for (var formatMessage = this, args = arguments, i = args.length; --i >= 0;)
                formatMessage = formatMessage.replace(regexes[i] || (regexes[i] = RegExp("\\{" + (i) + "\\}", "gm")), args[i]);
            return formatMessage;
        };
        if (!String.format) {
            String.format = function (formatMessage, params) {
                for (var args = arguments, i = args.length; --i;)
                    formatMessage = formatMessage.replace(regexes[i - 1] || (regexes[i - 1] = RegExp("\\{" + (i - 1) + "\\}", "gm")), args[i]);
                return formatMessage;
            };
        }
    }
})();
DATEx2
  • 3,585
  • 1
  • 24
  • 26
  • Looks okay, but you should surely namespace the variable, 'regexes', so it's not globally available. – roydukkey Mar 01 '13 at 18:51
  • Just wrapped it in an anonymous self executed function – DATEx2 Mar 02 '13 at 19:46
  • Right. I didn't know know the solution you preferred. I might have gone with something like `String.format.cache`. Nevertheless, to each his own. Thanks. – roydukkey Mar 04 '13 at 14:50
2

Here's a solution that just works with String.prototype:

String.prototype.format = function() {
    var s = this;
    for (var i = 0; i < arguments.length; i++) {       
        var reg = new RegExp("\\{" + i + "\\}", "gm");             
        s = s.replace(reg, arguments[i]);
    }
    return s;
}
Jonathan
  • 32,202
  • 38
  • 137
  • 208
2
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
            ;
        });
    };
}

Usage:

'{0}-{1}'.format('a','b');
// Result: 'a-b'

JSFiddle

Mikael Engver
  • 4,634
  • 4
  • 46
  • 53
1

Here is an solution that allows both prototype and function options.

// --------------------------------------------------------------------
// Add prototype for 'String.format' which is c# equivalent
//
// String.format("{0} i{2}a night{1}", "This", "mare", "s ");
// "{0} i{2}a night{1}".format("This", "mare", "s ");
// --------------------------------------------------------------------

if(!String.format)
    String.format = function(){
        for (var i = 0, args = arguments; i < args.length - 1; i++)
            args[0] = args[0].replace("{" + i + "}", args[i + 1]);
        return args[0];
    };
if(!String.prototype.format && String.format)
    String.prototype.format = function(){
        var args = Array.prototype.slice.call(arguments).reverse();
        args.push(this);
        return String.format.apply(this, args.reverse())
    };

Enjoy.

roydukkey
  • 3,149
  • 2
  • 27
  • 43
  • My intuition leads me to assume it wouldn't be better because it is using regex replacement, but the other way to be sure would be to profile the two. One can only guess for now. ;) – roydukkey Jul 27 '17 at 20:34
1

Aside from the fact that you are modifying the String prototype, there is nothing wrong with the function you provided. The way you would use it is this way:

"Hello {0},".format(["Bob"]);

If you wanted it as a stand-alone function, you could alter it slightly to this:

function format(string, object) {
    return string.replace(/{([^{}]*)}/g,
       function(match, group_match)
       {
           var data = object[group_match];
           return typeof data === 'string' ? data : match;
       }
    );
}

Vittore's method is also good; his function is called with each additional formating option being passed in as an argument, while yours expects an object.

What this actually looks like is John Resig's micro-templating engine.

Sean Vieira
  • 155,703
  • 32
  • 311
  • 293
  • +1: After re-reading this entry, Sean beat me to the "your function already does this" answer. If you pass an array (as Sean's example shows) to the format function, index [0] will map to "Bob". – David Mar 28 '10 at 23:11
1

Your function already takes a JSON object as a parameter:

string format = "Hi {foo}".replace({
    "foo": "bar",
    "fizz": "buzz"
});

if you notice, the code:

var r = o[b];

looks at your parameter (o) and uses a key-value-pairs within it to resolve the "replace"

David
  • 345
  • 1
  • 4
  • Since you are defining a String function prototype, you may run into problems if 3rd party JS on your page attempts to do the same (unlikely but possible). – David Mar 28 '10 at 22:57
  • I think that is exactly it. How could I search for this in my code to see if I am using it. Like in VS javascript files you can't go search for all references and the only ones I seen so far are Jquery.format what I believe is different. – chobo2 Mar 28 '10 at 23:26
  • Multi-file find (in each HTML, PHP/ASP/JSP, and JS) for ".replace" (you may need to escape the "." if you are doing a RegEx). False positives will be applied to JQuery objects (which are not strings themselves). – David Mar 29 '10 at 05:09
1

I just started porting Java's String.format() to JavaScript. You might find it useful too.

It supports basic stuff like this:

StringFormat.format("Hi %s, I like %s", ["Rob", "icecream"]);

Which results in

Hi Rob, I like icecream.

But also more advanced numberic formatting and date formatting like:

StringFormat.format("Duke's Birthday: %1$tA %1$te %1$tB, %1$tY", [new Date("2014-12-16")]);

Duke's Birthday: Tuesday 16 December, 2014

See for more in the examples.

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

Rob Audenaerde
  • 19,195
  • 10
  • 76
  • 121
1

Here are my two cents:

function stringFormat(str) {
  if (str !== undefined && str !== null) {
    str = String(str);
    if (str.trim() !== "") {
      var args = arguments;
      return str.replace(/(\{[^}]+\})/g, function(match) {
        var n = +match.slice(1, -1);
        if (n >= 0 && n < args.length - 1) {
          var a = args[n + 1];
          return (a !== undefined && a !== null) ? String(a) : "";
        }
        return match;
      });
    }
  }
  return "";
}

alert(stringFormat("{1}, {0}. You're looking {2} today.",
  "Dave", "Hello", Math.random() > 0.5 ? "well" : "good"));
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
jramos
  • 128
  • 1
  • 6
1
//Add "format" method to the string class
//supports:  "Welcome {0}. You are the first person named {0}".format("David");
//       and "First Name:{} Last name:{}".format("David","Wazy");
//       and "Value:{} size:{0} shape:{1} weight:{}".format(value, size, shape, weight)
String.prototype.format = function () {
    var content = this;
    for (var i = 0; i < arguments.length; i++) {
        var target = '{' + i + '}';
        content=content.split(target).join(String(arguments[i]))
        content = content.replace("{}", String(arguments[i]));
    }
    return content;
}
alert("I {} this is what {2} want and {} works for {2}!".format("hope","it","you"))

You can mix and match using positional and "named" replacement locations using this function.

davidWazy
  • 61
  • 6
0

Use sprintf library

Here you have a link where you can find the features of this library.

Alfredo A.
  • 1,697
  • 3
  • 30
  • 43
0
String.prototype.format = function () {
    var formatted = this;
    for (var arg in arguments) {
        formatted = formatted.split('{' + arg + '}').join(arguments[arg]);
    }
    return formatted;
};

USAGE:

'Hello {0}!'.format('Word')                 ->     Hello World!

'He{0}{0}o World!'.format('l')            ->     Hello World!

'{0} {1}!'.format('Hello', 'Word')     ->     Hello World!

'{0}!'.format('Hello {1}', 'Word')     ->     Hello World!

Cyrus
  • 2,261
  • 2
  • 22
  • 37