Equivalente de String.format en jQuery

Respuestas:

193

El código fuente de ASP.NET AJAX está disponible para su referencia, por lo que puede seleccionarlo e incluir las partes que desea continuar usando en un archivo JS separado. O puede portarlos a jQuery.

Aquí está la función de formato ...

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

Y aquí están los extremos con y comienza con funciones prototipo ...

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);
}
Josh Stodola
fuente
2
No parece que haya mucho. La versión de JavaScript no tiene todas las cosas elegantes de formato de números, obviamente. blog.stevex.net/index.php/string-formatting-in-csharp
Nosredna
Wow, en realidad ya pensé en esto, pero también pensé que no era posible debido a la licencia, no sabía que lo lanzaron bajo la Licencia Permisiva de Microsoft, muchas gracias por esto
Waleed Eissa
23
Con licencia o sin licencia ... solo hay una manera correcta de escribir algo tan simple
adamJLev
1
Construir (y luego descartar) un objeto RegEx para cada argumento cada vez que se llama al formato podría sobrecargar al recolector de basura.
mckoss
14
Advertencia: Esto se formateará de manera recursiva: por lo tanto, si tiene {0}{1}, {0}se reemplazará primero, y luego se reemplazarán todas las apariciones {1}tanto en el texto ya sustituido como en el formato original.
Zenexer 01 de
147

Esta es una variación más rápida / más simple (y prototípica) de la función que Josh publicó:

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

Uso:

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

Lo uso tanto que lo alias solo f, pero también puedes usar el más detallado format. p.ej'Hello {0}!'.format(name)

adamJLev
fuente
1
Con los navegadores modernos hay enfoques aún más simples: stackoverflow.com/a/41052964/120296
david
3
@david Las cadenas de plantillas NO son lo mismo en absoluto. Son azúcar sintáctico para la concatenación de cadenas, lo cual es bueno pero no es lo mismo que formatear. Se ejecutan instantáneamente, por lo tanto, los reemplazos deben estar dentro del alcance en el punto de definición. No puede almacenarlos en un archivo de texto o base de datos debido a esto. De hecho, la única forma de almacenarlos en cualquier lugar es ponerlos en una función. Una función de cadena formateada toma reemplazos posicionales que no se preocupan por el nombre de la variable.
krowe2
131

Muchas de las funciones anteriores (excepto las de Julian Jelfs) contienen el siguiente error:

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

O, para las variantes que cuentan hacia atrás desde el final de la lista de argumentos:

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

Aquí hay una función correcta. Es una variante prototípica del código de Julian Jelfs, que hice un poco más estricto:

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

Y aquí hay una versión un poco más avanzada de la misma, que le permite escapar de las llaves duplicándolas:

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

Esto funciona correctamente:

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

Aquí hay otra buena implementación de Blair Mitchelmore, con un montón de buenas características adicionales: https://web.archive.org/web/20120315214858/http://blairmitchelmore.com/javascript/string.format

gpvos
fuente
Y otra, que no he mirado muy de cerca, pero que parece aplicar formatos como {0: + $ #, 0,00; - $ #, 0,00; 0}: masterdata.dyndns.org/r/string_format_for_javascript
gpvos
Ooh, y uno que usa el formato de interpolación de Python: code.google.com/p/jquery-utils/wiki/…
gpvos
La aplicación de formato que he mencionado dos comentarios arriba se ha trasladado a masterdata.se/r/string_format_for_javascript
gpvos
Nota: la respuesta de ianj en otra parte de esta página le permite utilizar parámetros con nombre en lugar de numéricos. Si cambia su método por uno que use prototipos, deberá cambiar el segundo parámetro a la función slice.call de 1 a 0.
gpvos
48

Hizo una función de formato que toma una colección o una matriz como argumentos

Uso:

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

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

Código:

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];
    });
};
ianj
fuente
55
Agradable, todo lo que falta es: String.prototype.format = function (col) {return format (this, col);}
Erik
10
prefiero no extender la cadena
ianj
3
hay un pequeño error tipográfico en el uso: la segunda línea debe ser: formato ("puedo hablar {0} ya que era {1}", 'javascript', 10);
Guillaume Gendre
¿Por qué no preferir extender cadena usando String.prototype?
Kiquenet
36

Hay una opción (algo) oficial: jQuery.validator.format .

Viene con jQuery Validation Plugin 1.6 (al menos).
Muy similar al String.Formatencontrado en .NET.

Editar enlace roto fijo.

rsenna
fuente
13

Aunque no es exactamente lo que estaba pidiendo la Q, he creado uno que es similar pero usa marcadores de posición con nombre en lugar de numerados. Personalmente prefiero tener argumentos con nombre y simplemente enviar un objeto como argumento (más detallado, pero más fácil de mantener).

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

Aquí hay un ejemplo de uso ...

alert("Hello {name}".format({ name: 'World' }));
Brian
fuente
8

Con un navegador moderno, compatible con EcmaScript 2015 (ES6), puede disfrutar de Template Strings . En lugar de formatear, puede inyectarle directamente el valor de la variable:

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

Tenga en cuenta que la cadena de la plantilla debe escribirse usando ticks de retroceso (`).

david
fuente
6

Ninguna de las respuestas presentadas hasta ahora no tiene una optimización obvia del uso del gabinete para inicializar una vez y almacenar expresiones regulares, para usos posteriores.

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

Además, ninguno de los ejemplos respeta la implementación de formato () si ya existe uno.


fuente
2
rx2 es innecesario, vea mi implementación; tienes (paréntesis) en rx1, pero no utilices el valor que pasan a la función interna. Además, creo que esta es una optimización obvia para hacer en el motor Javascript . ¿Estás seguro de que los navegadores modernos no hacen esta optimización detrás de escena? Perl lo hizo en 1990. Tiene razón en que debería haber un contenedor alrededor de la función para verificar si ya está implementada.
gpvos
1
Además, la prueba en (args [idx] === "") me parece superflua: ya está cubierta por la primera prueba en esa línea, ¡porque no está definida! == "".
gpvos
4

Aquí está el mío:

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

No es a prueba de balas, pero funciona si lo usa con sensatez.

Julian Jelfs
fuente
4

Mucho más allá de la temporada tardía, pero acabo de mirar las respuestas dadas y mi tuppence vale la pena:

Uso:

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

Método:

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

Resultado:

"aalert" is not defined
3.14 3.14 a{2}bc foo
RickL
fuente
3

Ahora puede usar 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);

Arek Kostrzeba
fuente
3
Estaba entusiasmado con los literales de plantilla hasta que vi que solo funcionan cuando la cadena se define junto con las variables de reemplazo. Para mí eso los hace prácticamente inútiles; por una razón u otra, la mayoría de mis cadenas se definen por separado del código que las completa / usa.
piedra
2

Aquí está mi versión que puede escapar de '{' y limpiar esos marcadores de posición no asignados.

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);
}
Feng
fuente
2

La siguiente respuesta es probablemente la más eficiente, pero tiene la advertencia de que solo es adecuada para asignaciones de argumentos 1 a 1. Utiliza la forma más rápida de concatenar cadenas (similar a un generador de cadenas: matriz de cadenas, unidas). Este es mi propio código. Sin embargo, probablemente necesite un mejor separador.

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

Úselo como:

alert(String.format("<a href='~'>~</a>", ["one", "two"]));
Skychan
fuente
2
La respuesta aceptada es la mejor respuesta. Estoy dando una respuesta única que es útil en escenarios en los que desea exprimir cada bit de eficiencia posible (bucles largos) y tiene una asignación 1: 1 de args. Reemplazar cuz más eficiente () y la nueva Regex () junto con la realización de la expresión regular definitivamente usa más ciclos de CPU que la división simple ().
Skychan
No hay necesidad de -1. No, no es ampliamente aplicable, pero tiene razón, esto sería un poco más eficiente. Ahora, para el autor, una pregunta de arquitectura: ¿qué tipo de aplicación trataría con un conjunto de datos tan grande en el cliente que se requeriría este tipo de optimización?
Michael Blackburn
Buen punto Michael Blackburn. La mayoría de las situaciones probablemente estén bien. En mi caso, estaba tratando con una buena porción de datos (variaciones de productos dentro de un grupo de productos, por lo que posiblemente cientos de variantes) y mi opinión es que, dado que los usuarios suelen tener abiertas muchas pestañas del navegador y cada sitio web tiende a absorber una gran cantidad de CPU que Preferí esta implementación para mantener una alta eficiencia.
Skychan
2

Esto viola el principio DRY, pero es una solución concisa:

var button = '<a href="{link}" class="btn">{text}</a>';
button = button.replace('{text}','Authorize on GitHub').replace('{link}', authorizeUrl);
ilyaigpetrov
fuente
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>
Kishor Dalwadi
fuente
Esto está bien para los URI, pero para uso general, tener una cadena que contenga tanto el formato como los componentes es muy frágil. ¿Qué pasa si su cadena contiene comas?
Michael Blackburn
0

No pude obtener la respuesta de Josh Stodola para trabajar, pero lo siguiente funcionó para mí. Tenga en cuenta la especificación de prototype. (Probado en IE, FF, Chrome y 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;
}

sen realidad debería ser un clon de thisfin de no ser un método destructivo, pero no es realmente necesario.

JellicleCat
fuente
No es un método destructivo. Cuando s se reasigna con el valor de retorno de s.replace (), esto permanece intacto.
gpvos
0

Ampliando la gran respuesta de adamJLev anterior , aquí está la versión de TypeScript:

// 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;
};
Annie
fuente
0

Tengo un plunker que lo agrega al prototipo de cadena: string.format No es tan corto como algunos de los otros ejemplos, sino que es mucho más flexible.

El uso es similar a la versión de C #:

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

Además, se agregó soporte para usar nombres y propiedades de objetos

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
ShrapNull
fuente
0

También puede cerrar la matriz con reemplazos como este.

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

o puedes probar bind

'/getElement/_/_/_'.replace(/_/g, (function(_) {return this.ar[this.i++];}).bind({ar: ["invoice", "id", 1337],i: 0}))
test30
fuente