Formatar uma string JavaScript usando espaços reservados e um objeto de substituições?


91

Eu tenho uma string com dizer: My Name is %NAME% and my age is %AGE%.

%XXX%são marcadores de posição. Precisamos substituir valores ali de um objeto.

O objeto se parece com: {"%NAME%":"Mike","%AGE%":"26","%EVENT%":"20"}

Preciso analisar o objeto e substituir a string pelos valores correspondentes. Portanto, esse resultado final será:

Meu nome é Mike e tenho 26 anos.

Tudo deve ser feito usando puro javascript ou jquery.


2
Parece mais um objeto do que uma matriz
Joel Coehoorn

3
O que você tentou até agora? Você já olhou para o método string .replace () ? (Além disso, você não tem uma matriz lá, você tem um objeto.)
nnnnnn

1
Isso é muito feio. Certamente você estaria tão bem servido por {NAME: "Mike", AGE: 26, EVENT: 20}? Você ainda exigiria que essas chaves apareçam delimitadas por sinais de porcentagem na string de entrada, é claro.
davidchambers

Respostas:


144

Os requisitos da pergunta original claramente não poderiam se beneficiar da interpolação de strings, pois parece que é um processamento em tempo de execução de chaves de substituição arbitrárias.

No entanto , se você apenas teve que fazer interpolação de string, você pode usar:

const str = `My name is ${replacements.name} and my age is ${replacements.age}.`

Observe os crases que delimitam a string, eles são obrigatórios.


Para obter uma resposta adequada aos requisitos específicos do OP, você pode usar String.prototype.replace() para as substituições.

O código a seguir tratará todas as correspondências e não as alterará sem uma substituição (desde que os valores de substituição sejam todos strings, caso contrário, veja abaixo).

var replacements = {"%NAME%":"Mike","%AGE%":"26","%EVENT%":"20"},
    str = 'My Name is %NAME% and my age is %AGE%.';

str = str.replace(/%\w+%/g, function(all) {
   return replacements[all] || all;
});

jsFiddle .

Se algumas de suas substituições não forem strings, certifique-se de que elas existam primeiro no objeto. Se você tiver um formato como o exemplo, ou seja, envolto em sinais de porcentagem, você pode usar oin operador para fazer isso.

jsFiddle .

No entanto, se seu formato não tiver um formato especial, ou seja, qualquer string, e seu objeto de substituição não tiver um nullprotótipo, use Object.prototype.hasOwnProperty(), a menos que você possa garantir que nenhuma de suas possíveis substrings substituídas entrará em conflito com nomes de propriedade no protótipo.

jsFiddle .

Caso contrário, se sua string de substituição fosse 'hasOwnProperty', você obteria uma string bagunçada resultante.

jsFiddle .


Como uma nota lateral, você deve ser chamado replacementsum Object, e não um Array.


2
+1. Agradável. Embora você possa querer dizer return replacements[all] || allpara cobrir %NotInReplacementsList%casos.
nnnnnn

5
Isso não funcionará se o valor de substituição for falso. Portanto, é melhor usar esta declaração de retorno:return all in params ? params[all] : all;
Michael Härtl

@ MichaelHärtl Não deveriam todas as suas substituições ser strings? Se você deseja substituir por uma string vazia, é melhor verificar por outros meios.
alex de

1
@alex Tive a situação em que as substituições também podiam ser inteiros e até mesmo 0. Neste caso, não funcionou.
Michael Härtl

@ MichaelHärtl Atualizado para cobrir esse caso.
alex de

23

Que tal usar literais de modelo ES6?

var a = "cat";
var b = "fat";
console.log(`my ${a} is ${b}`); //notice back-ticked string

Mais sobre literais de modelo ...


5
Se você tem um objeto com marcadores de posição como o OP, como a interpolação de string ajuda nisso?
alex

Funciona como um encanto! A solução mais pragmática para meus problemas de espaço reservado, perfeita.
BAERUS

Esta solução não funciona para substituições de tempo de execução
Thierry J.

12

Você pode usar JQuery (jquery.validate.js) para fazê-lo funcionar facilmente.

$.validator.format("My name is {0}, I'm {1} years old",["Bob","23"]);

Ou se você quiser usar apenas esse recurso, você pode definir essa função e usá-la como

function format(source, params) {
    $.each(params,function (i, n) {
        source = source.replace(new RegExp("\\{" + i + "\\}", "g"), n);
    })
    return source;
}
alert(format("{0} is a {1}", ["Michael", "Guy"]));

crédito para a equipe jquery.validate.js


1
Você definitivamente não gostaria de carregar esse plugin apenas para isso, mas já estou usando isso para validar um formulário na página ... então, obrigado pela dica!
nabrown

1
Bastante ineficiente para criar uma regex para cada número, seria melhor combinar todos os números e então substituir se o valor fosse encontrado na matriz, talvez?
alex de


1
+ muito bom ... e para $.eachvocê String.prototype.format=function(p){var s=this,r=function(v,i){s=s.replace(new RegExp("\\{"+i+"\\}","g"),v);};p.forEach(r);return s;}, não precisa incluir jquery apenas para aquele;)
Larphoid

11

Tal como acontece com navegador moderno, espaço reservado é suportado pela nova versão do Chrome / Firefox, semelhante como a função de estilo C printf().

Marcadores de posição:

  • %s Corda.
  • %d, %iNúmero inteiro.
  • %f Número de ponto flutuante.
  • %o Hiperlink do objeto.

por exemplo

console.log("generation 0:\t%f, %f, %f", a1a1, a1a2, a2a2);

BTW, para ver a saída:

  • No Chrome, use o atalho Ctrl + Shift + Jou F12para abrir a ferramenta de desenvolvedor.
  • No Firefox, use o atalho Ctrl + Shift + Kou F12para abrir a ferramenta de desenvolvedor.

@Update - suporte a nodejs

Parece que nodejs não oferece suporte %f, em vez disso, poderia usar %dem nodejs. Com %dnúmero será impresso como número flutuante, não apenas inteiro.



5

Você pode usar uma função de substituição personalizada como esta:

var str = "My Name is %NAME% and my age is %AGE%.";
var replaceData = {"%NAME%":"Mike","%AGE%":"26","%EVENT%":"20"};

function substitute(str, data) {
    var output = str.replace(/%[^%]+%/g, function(match) {
        if (match in data) {
            return(data[match]);
        } else {
            return("");
        }
    });
    return(output);
}

var output = substitute(str, replaceData);

Você pode ver o funcionamento aqui: http://jsfiddle.net/jfriend00/DyCwk/ .


1
Legal, Alex fez quase exatamente a mesma coisa, mas em menos linhas de código (embora os operadores ternários sejam provavelmente mais lentos do que if..else).
RobG de

Ei, eu te dei um +1! Vocês dois fizeram uma função de substituição, o seu não é exatamente o mesmo, mas muito semelhante. Sua RegExp também é diferente, o OP ficaria melhor usando %% ou $$ ou semelhante como delimitadores - um único% ou% provavelmente ocorrerá em uma string normalmente, mas os duplos são improváveis.
RobG

4

Se você quiser fazer algo mais próximo do console.log, como substituir% s marcadores de posição, como em

>console.log("Hello %s how are you %s is everything %s?", "Loreto", "today", "allright")
>Hello Loreto how are you today is everything allright?

eu escrevi isto

function log() {
  var args = Array.prototype.slice.call(arguments);
  var rep= args.slice(1, args.length);
  var i=0;
  var output = args[0].replace(/%s/g, function(match,idx) {
    var subst=rep.slice(i, ++i);
    return( subst );
  });
   return(output);
}
res=log("Hello %s how are you %s is everything %s?", "Loreto", "today", "allright");
document.getElementById("console").innerHTML=res;
<span id="console"/>

você vai ter

>log("Hello %s how are you %s is everything %s?", "Loreto", "today", "allright")
>"Hello Loreto how are you today is everything allright?"

ATUALIZAR

Eu adicionei uma variante simples como String.prototypeútil ao lidar com transformações de string, aqui está:

String.prototype.log = function() {
    var args = Array.prototype.slice.call(arguments);
    var rep= args.slice(0, args.length);
    var i=0;
    var output = this.replace(/%s|%d|%f|%@/g, function(match,idx) {
      var subst=rep.slice(i, ++i);
      return( subst );
    });
    return output;
   }

Nesse caso, você fará

"Hello %s how are you %s is everything %s?".log("Loreto", "today", "allright")
"Hello Loreto how are you today is everything allright?"

Experimente esta versão aqui


2
Fiz uma variação em sua função sem protótipos, formatMessage(message: string, values: string[]) { let i = 0; return message.replace(/%\w+%/g, (match, idx) => { return values[i++]; }); } isso leva a mensagem para o formato e a matriz de valores de substituição e procura%SOME_VALUE%
Baku

2

Isso permite que você faça exatamente isso

NPM: https://www.npmjs.com/package/stringinject

GitHub: https://github.com/tjcafferkey/stringinject

Fazendo o seguinte:

var str = stringInject("My username is {username} on {platform}", { username: "tjcafferkey", platform: "GitHub" });

// My username is tjcafferkey on Git

2
Mas já que você pode fazer isso no ES6, isso provavelmente é um pouco exagero?
IonicBurger

1
Isso permite que você armazene a string em um local em um formato e, posteriormente, substitua os itens correspondentes usando apenas uma função. Que eu saiba, isso não é possível fazer com os literais de modelo es6. Um uso para isso seria, por exemplo, strings de tradução em que você consumirá a string em outro lugar e injetará os valores desejados lá.
Johan Persson

2

Como um exemplo rápido:

var name = 'jack';
var age = 40;
console.log('%s is %d yrs old',name,age);

O resultado é:

jack tem 40 anos


6
Isso é ótimo, a menos que você queira fazer algo além de registrá-lo no console.
alex de

13
não deveria ser console.log?
drzaus

2

Aqui está outra maneira de fazer isso usando literais de modelo es6 dinamicamente em tempo de execução.

const str = 'My name is ${name} and my age is ${age}.'
const obj = {name:'Simon', age:'33'}


const result = new Function('const {' + Object.keys(obj).join(',') + '} = this.obj;return `' + str + '`').call({obj})

document.body.innerHTML = result


0
const stringInject = (str = '', obj = {}) => {
  let newStr = str;
  Object.keys(obj).forEach((key) => {
    let placeHolder = `#${key}#`;
    if(newStr.includes(placeHolder)) {
      newStr = newStr.replace(placeHolder, obj[key] || " ");
    }
  });
  return newStr;
}
Input: stringInject("Hi #name#, How are you?", {name: "Ram"});
Output: "Hi Ram, How are you?"

0

Eu escrevi um código que permite formatar strings facilmente.

Use esta função.

function format() {
    if (arguments.length === 0) {
        throw "No arguments";
    }
    const string = arguments[0];
    const lst = string.split("{}");
    if (lst.length !== arguments.length) {
        throw "Placeholder format mismatched";
    }
    let string2 = "";
    let off = 1;
    for (let i = 0; i < lst.length; i++) {
        if (off < arguments.length) {
            string2 += lst[i] + arguments[off++]
        } else {
            string2 += lst[i]
        }
    }
    return string2;
}

Exemplo

format('My Name is {} and my age is {}', 'Mike', 26);

Resultado

Meu nome é Mike e tenho 26 anos


0

Atualmente ainda não existe uma solução nativa em Javascript para este comportamento. Os modelos marcados são algo relacionado, mas não resolvem.

Aqui há um refator da solução de alex com um objeto para substituições.

A solução usa funções de seta e uma sintaxe semelhante para os marcadores de posição como a interpolação Javascript nativa em literais de modelo (em {}vez de %%). Além disso, não há necessidade de incluir delimitadores ( %) nos nomes das substituições.

Existem dois sabores: descritivo e reduzido.

Solução descritiva:

const stringWithPlaceholders = 'My Name is {name} and my age is {age}.';

const replacements = {
  name: 'Mike',
  age: '26',
};

const string = stringWithPlaceholders.replace(
  /{\w+}/g,
  placeholderWithDelimiters => {
    const placeholderWithoutDelimiters = placeholderWithDelimiters.substring(
      1,
      placeholderWithDelimiters.length - 1,
    );
    const stringReplacement = replacements[placeholderWithoutDelimiters] || placeholderWithDelimiters;
    return stringReplacement;
  },
);

console.log(string);

Solução reduzida:

const stringWithPlaceholders = 'My Name is {name} and my age is {age}.';

const replacements = {
  name: 'Mike',
  age: '26',
};

const string = stringWithPlaceholders.replace(/{\w+}/g, placeholder =>
  replacements[placeholder.substring(1, placeholder.length - 1)] || placeholder,
);

console.log(string);

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.