Equivalente a String.format em jQuery

194

Estou tentando mover algum código JavaScript do MicrosoftAjax para o JQuery. Eu uso os equivalentes JavaScript no MicrosoftAjax dos métodos .net populares, por exemplo, String.format (), String.startsWith (), etc. Existem equivalentes a eles no jQuery?

Waleed Eissa
fonte

Respostas:

193

O código-fonte do ASP.NET AJAX está disponível para sua referência, para que você possa selecioná-lo e incluir as partes que deseja continuar usando em um arquivo JS separado. Ou, você pode portá-los para jQuery.

Aqui está a função 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;
}

E aqui estão as funções protótipo endsWith e beginWith ...

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
fonte
2
Não parece que há muito nisso. A versão JavaScript não tem todo o material de formatação de números sofisticados, obviamente. blog.stevex.net/index.php/string-formatting-in-csharp
Nosredna
Uau, eu realmente já pensei sobre isso, mas também achei que não foi possível por causa da licença, não sabia que eles liberado sob Microsoft Permissive License, muito obrigado por este
Waleed Eissa
23
Licença ou sem licença .. só há uma maneira certa de escrever algo tão simples
adamJLev
1
Construir (e depois descartar) um objeto RegEx para cada argumento a cada vez que o formato é chamado pode sobrecarregar o coletor de lixo.
Mckoss 18/03
14
Aviso: O formato será recursivo: portanto, se você tiver {0}{1}, {0}será substituído primeiro e, em seguida, todas as ocorrências {1}no texto já substituído e no formato original serão substituídas.
Zenexer 01/07
147

Essa é uma variação mais rápida / mais simples (e prototípica) da função que Josh postou:

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) 

Eu uso tanto isso que eu o aliastei apenas f, mas você também pode usar os mais detalhados format. por exemplo'Hello {0}!'.format(name)

adamJLev
fonte
1
Com navegadores modernos existem abordagens ainda mais simples: stackoverflow.com/a/41052964/120296
david
3
@David String de modelo NÃO é a mesma coisa, realmente. Eles são açúcar sintático para concatenação de strings, o que é bom, mas não o mesmo que formatação. Eles são executados instantaneamente, portanto, as substituições devem estar no escopo no ponto de definição. Você não pode armazená-los em um arquivo de texto ou banco de dados por causa disso. De fato, a única maneira de armazená-los em qualquer lugar é colocá-los em uma função. Uma função de string formatada recebe substituições posicionais que não se importam com o nome da variável.
precisa saber é o seguinte
131

Muitas das funções acima (exceto as de Julian Jelfs) contêm o seguinte erro:

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

Ou, para as variantes que contam de trás para frente no final da lista de argumentos:

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

Aqui está uma função correta. É uma variante prototípica do código de Julian Jelfs, que eu fiz um pouco mais:

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

E aqui está uma versão um pouco mais avançada do mesmo, que permite escapar dos aparelhos dobrando-os:

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

Isso funciona corretamente:

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

Aqui está outra boa implementação de Blair Mitchelmore, com vários recursos extras agradáveis: https://web.archive.org/web/20120315214858/http://blairmitchelmore.com/javascript/string.format

gpvos
fonte
E outro, que eu não olhei muito de perto, mas que parece implementar formatos como {0: + $ #, 0.00; - $ #, 0.00; 0}: masterdata.dyndns.org/r/string_format_for_javascript
gpvos
Ooh, e um que usa o formato de interpolação Python: code.google.com/p/jquery-utils/wiki/…
gpvos
A implementação formato mencionei dois comentários se se mudou para masterdata.se/r/string_format_for_javascript
gpvos
Nota: a resposta de ianj em outras partes desta página permite que você use parâmetros nomeados em vez de numéricos. Se você alterar o seu método em um que utiliza protótipos, você terá que alterar o segundo parâmetro para a função slice.call de 1 a 0.
gpvos
48

Criou uma função de formato que usa uma coleção ou uma 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
fonte
5
Nice one, tudo o que está faltando é: String.prototype.format = function (col) {formato de retorno (isto, col);}
Erik
10
eu prefiro não estender corda
ianj
3
há um pequeno erro de digitação no uso: a segunda linha deve ser: format ("eu posso falar {0} desde que eu era {1}", 'javascript', 10);
Guillaume Gendre
Por que não preferir estender string usando String.prototype?
Kiquenet
36

Existe uma opção (um tanto) oficial: jQuery.validator.format .

Vem com o jQuery Validation Plugin 1.6 (pelo menos).
Muito semelhante ao String.Formatencontrado no .NET.

Editar Link quebrado quebrado.

rsenna
fonte
13

Embora não seja exatamente o que o Q estava pedindo, criei um que é semelhante, mas usa espaços reservados nomeados em vez de numerados. Pessoalmente, prefiro ter argumentos nomeados e apenas enviar um objeto como argumento (mais detalhado, mas mais fácil de manter).

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

Aqui está um exemplo de uso ...

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

Usando um navegador moderno, que suporta o EcmaScript 2015 (ES6), você pode aproveitar as Strings de modelo . Em vez de formatar, você pode injetar diretamente o valor da variável nele:

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

Observe que a string do modelo deve ser escrita usando back-ticks (`).

david
fonte
6

Nenhuma das respostas apresentadas até o momento não possui otimização óbvia do uso do gabinete para inicializar uma vez e armazenar expressões regulares, para usos subsequentes.

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

Além disso, nenhum dos exemplos respeita a implementação format () se já existir.


fonte
2
rx2 é desnecessário, veja minha implementação; você tem (parênteses) em rx1, mas não use o valor que eles passam para a função interna. Além disso, acho que essa é uma otimização óbvia para fazer no mecanismo Javascript . Você tem certeza de que os navegadores modernos ainda não fazem essa otimização nos bastidores? O Perl fez isso em 1990. Você está certo de que deve haver um wrapper em torno da função para verificar se ela já está implementada.
Gpvos 17/08/2012
1
Além disso, o teste em (args [idx] === "") parece supérfluo para mim: já está coberto pelo primeiro teste nessa linha, porque indefinido! == "".
gpvos
4

Aqui está o meu:

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

Não é à prova de balas, mas funciona se você usá-lo com sensibilidade.

Julian Jelfs
fonte
4

Passado o final da temporada, mas eu apenas estive olhando as respostas dadas e tenho meu valor tuppence:

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
fonte
3

Agora você pode usar Literais de Modelo :

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
fonte
3
Fiquei empolgado com os literais de modelo até perceber que eles só funcionam quando a string é definida ao lado das variáveis ​​de substituição. Para mim, isso os torna praticamente inúteis; por um motivo ou outro, a maioria das minhas strings é definida separadamente do código que as preenche / as utiliza.
stone
2

Aqui está minha versão capaz de escapar do '{' e limpar os marcadores de posição não atribuídos.

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
fonte
2

A resposta a seguir é provavelmente a mais eficiente, mas tem a ressalva de ser adequada apenas para mapeamentos de argumentos 1 a 1. Isso usa a maneira mais rápida de concatenar cadeias (semelhante a um construtor de cadeias: matriz de cadeias, unida). Este é o meu próprio código. Provavelmente precisa de um separador melhor.

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-o como:

alert(String.format("<a href='~'>~</a>", ["one", "two"]));
Skychan
fonte
2
Resposta aceita é a melhor resposta. Estou dando uma resposta única que é útil em cenários em que você deseja extrair todo o possível de eficiência (loops longos) e você tem um mapeamento 1: 1 de args. Mais eficiente, porque replace () e o novo Regex (), juntamente com a execução do regex, definitivamente usam mais ciclos de CPU que o single split ().
Skychan
Não há necessidade de -1. Não, não é amplamente aplicável, mas ele está correto, isso seria um pouco mais eficiente. Agora, para o autor, uma questão de arquitetura: que tipo de aplicativo estaria lidando com um conjunto de dados tão grande no cliente que esse tipo de otimização seria necessário?
Michael Blackburn
Bom ponto Michael Blackburn. A maioria das situações provavelmente está bem. No meu caso, eu estava lidando com uma boa porção de dados (variações de produtos em um grupo de produtos, possivelmente centenas de variantes) e minha opinião é que, como os usuários geralmente têm muitas guias do navegador abertas e todo site tende a consumir muita CPU que Eu preferi essa implementação para manter alta eficiência.
Skychan
2

Isso viola o princípio DRY, mas é uma solução concisa:

var button = '<a href="{link}" class="btn">{text}</a>';
button = button.replace('{text}','Authorize on GitHub').replace('{link}', authorizeUrl);
ilyaigpetrov
fonte
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
fonte
Isso é bom para URIs, mas para uso geral, ter uma sequência contém o formato e os componentes é muito quebradiço. E se a sua string contiver vírgulas?
Michael Blackburn
0

Não consegui que a resposta de Josh Stodola funcionasse, mas o seguinte funcionou para mim. Observe a especificação de prototype. (Testado no IE, FF, Chrome e 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;
}

srealmente deve ser um clone de thismodo a não ser um método destrutivo, mas não é realmente necessário.

JellicleCat
fonte
Não é um método destrutivo. Quando s é reatribuído com o valor de retorno de s.replace (), isso permanece intocado.
gpvos
0

Expandindo a excelente resposta de adamJLev acima , aqui está a versão 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
fonte
0

Eu tenho um plunker que o adiciona ao protótipo de string: string.format Não é tão curto quanto alguns dos outros exemplos, mas muito mais flexível.

O uso é semelhante à versão 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

Além disso, foi adicionado suporte ao uso de nomes e propriedades 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
fonte
0

Você também pode fechar a matriz com substituições como esta.

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

ou você pode tentar bind

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