Formattare una stringa JavaScript utilizzando segnaposto e un oggetto di sostituzioni?


92

Ho una stringa con dire: My Name is %NAME% and my age is %AGE%.

%XXX%sono segnaposto. Dobbiamo sostituire i valori lì da un oggetto.

L'oggetto ha questo aspetto: {"%NAME%":"Mike","%AGE%":"26","%EVENT%":"20"}

Devo analizzare l'oggetto e sostituire la stringa con i valori corrispondenti. Quindi l'output finale sarà:

Il mio nome è Mike e la mia età è 26.

L'intera operazione deve essere eseguita utilizzando puro javascript o jquery.


2
Sembra più un oggetto che un array
Joel Coehoorn

3
Cosa hai provato fino ad ora? Hai esaminato il metodo string .replace () ? (Inoltre, non hai un array lì, hai un oggetto.)
nnnnnn

1
È piuttosto brutto. Sicuramente saresti servito altrettanto bene {NAME: "Mike", AGE: 26, EVENT: 20}? È comunque necessario che queste chiavi appaiano bloccate dai segni di percentuale nella stringa di input, ovviamente.
davidchambers

Risposte:


151

I requisiti della domanda originale chiaramente non potevano beneficiare dell'interpolazione di stringhe, poiché sembra che si tratti di un'elaborazione runtime di chiavi di sostituzione arbitrarie.

Tuttavia , se dovessi solo eseguire l'interpolazione delle stringhe, puoi usare:

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

Notare i backtick che delimitano la stringa, sono obbligatori.


Per una risposta che soddisfi i requisiti del particolare OP, è possibile utilizzare String.prototype.replace()per le sostituzioni.

Il codice seguente gestirà tutte le corrispondenze e non toccherà quelle senza una sostituzione (purché i valori di sostituzione siano tutte stringhe, in caso contrario, vedere di seguito).

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 alcune delle tue sostituzioni non sono stringhe, assicurati prima che siano presenti nell'oggetto. Se hai un formato come nell'esempio, cioè racchiuso in segni di percentuale, puoi utilizzare l' inoperatore per ottenere ciò.

jsFiddle .

Tuttavia, se il tuo formato non ha un formato speciale, cioè nessuna stringa, e il tuo oggetto di sostituzione non ha un nullprototipo, usalo Object.prototype.hasOwnProperty(), a meno che tu non possa garantire che nessuna delle tue potenziali sottostringhe sostituite si scontrerà con i nomi delle proprietà sul prototipo.

jsFiddle .

Altrimenti, se la stringa di sostituzione fosse 'hasOwnProperty', si otterrebbe una stringa confusa risultante.

jsFiddle .


Come nota a margine, dovresti essere chiamato replacementsun Object, non un Array.


2
+1. Bello. Anche se potresti voler dire return replacements[all] || alldi coprire i %NotInReplacementsList%casi.
nnnnnn

6
Questo non funzionerà se il valore di sostituzione è falso. Quindi è meglio usare questa dichiarazione di ritorno:return all in params ? params[all] : all;
Michael Härtl

@ MichaelHärtl Le tue sostituzioni non dovrebbero essere tutte stringhe? Se vuoi sostituire con una stringa vuota, meglio controllare con altri mezzi.
alex

1
@alex ho avuto la situazione in cui le sostituzioni potevano anche essere numeri interi e persino 0. In questo caso non ha funzionato.
Michael Härtl

@ MichaelHärtl Aggiornato per coprire quel caso.
alex

24

Che ne dici di usare i letterali modello ES6?

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

Ulteriori informazioni sui letterali modello ...


5
Se hai un oggetto con segnaposto come l'OP, in che modo l'interpolazione delle stringhe aiuta?
alex

Funziona come un fascino! Soluzione pragmatica ai miei problemi di collocazione, perfetta.
BAERUS

Questa soluzione non funziona per le sostituzioni in fase di esecuzione
Thierry J.

13

Puoi usare JQuery (jquery.validate.js) per farlo funzionare facilmente.

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

Oppure, se vuoi usare solo quella funzione, puoi definire quella funzione e usarla semplicemente come

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"]));

credito al team jquery.validate.js


1
Sicuramente non vorresti caricare questo plugin solo per questo, ma lo sto già usando per convalidare un modulo sulla pagina ... quindi grazie per il suggerimento!
nabrown

1
Abbastanza inefficiente creare una regex per ogni numero, sarebbe meglio abbinare tutti i numeri e poi sostituirli se il valore è stato trovato nell'array, forse?
alex


1
+ molto bello ... e per $.eachte potresti fare in 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;}modo da non dover includere jquery solo per quello;)
Larphoid

11

Come nel caso di browser moderno, segnaposto è supportato dalla nuova versione di Chrome / Firefox, simile a quella della funzione di stile C printf().

Segnaposto:

  • %s Corda.
  • %d, %iNumero intero.
  • %f Numero in virgola mobile.
  • %o Collegamento ipertestuale dell'oggetto.

per esempio

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

BTW, per vedere l'output:

  • In Chrome, utilizza il collegamento Ctrl + Shift + Jo F12per aprire lo strumento per sviluppatori.
  • In Firefox, utilizza il collegamento Ctrl + Shift + Ko F12per aprire lo strumento per sviluppatori.

@Update - supporto nodejs

Sembra che nodejs non supporti %f, invece, potrebbe usare%d in nodejs. Con %dnumero verrà stampato come numero mobile, non solo intero.



5

Puoi utilizzare una funzione di sostituzione personalizzata come questa:

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

Puoi vederlo funzionare qui: http://jsfiddle.net/jfriend00/DyCwk/ .


1
Fantastico, Alex ha fatto più o meno esattamente la stessa cosa ma in meno righe di codice (sebbene gli operatori ternari siano probabilmente più lenti di if..else).
RobG

Ehi, ti ho dato un +1! Entrambi avete eseguito una funzione di sostituzione, la vostra non è esattamente la stessa ma abbastanza simile. Anche la tua RegExp è diversa, l'OP sarebbe meglio usare %% o $$ o simili come delimitatori: è probabile che un singolo% o% si verifichi normalmente in una stringa, ma i doppi sono improbabili.
RobG

4

Se vuoi fare qualcosa di più vicino a console.log come sostituire i segnaposto di% s come in

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

Ho scritto questo

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"/>

otterrete

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

AGGIORNARE

Ho aggiunto una semplice variante String.prototypeutile quando si tratta di trasformazioni di stringhe, eccola:

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

In tal caso lo farai

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

Prova questa versione qui


2
ho apportato una variazione alla tua funzione senza prototipi, formatMessage(message: string, values: string[]) { let i = 0; return message.replace(/%\w+%/g, (match, idx) => { return values[i++]; }); } questo porta il messaggio nel formato e nella matrice dei valori di sostituzione e cerca%SOME_VALUE%
baku

4

Attualmente non esiste ancora una soluzione nativa in Javascript per questo comportamento. I modelli con tag sono qualcosa di correlato, ma non risolverlo.

Qui c'è un refactoring della soluzione di alex con un oggetto per le sostituzioni.

La soluzione utilizza funzioni freccia e una sintassi simile per i segnaposto come interpolazione Javascript nativa nei valori letterali del modello ( {}anziché %%). Inoltre non è necessario includere delimitatori ( %) nei nomi delle sostituzioni.

Ci sono due gusti: descrittivo e ridotto.

Soluzione descrittiva:

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

Soluzione ridotta:

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


1
Grazie per questo, è fantastico avere una soluzione generica per una stringa segnaposto e un oggetto da fondere in questo modo
Carlos P

@CarlosP, questa è l'idea, avere una soluzione generica. Ma penso che dovrebbe essere qualcosa di nativo della lingua, poiché è un caso d'uso comune.
AMS777

Questo può essere semplificato un po 'di più utilizzando gruppi di espressioni regolari come questo:stringWithPlaceholders.replace(/{(\w+)}/g, (fullMatch, group1) => replacements[group1] || fullMatch )
Kade

2

Questo ti permette di fare esattamente questo

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

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

In questo modo:

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

// My username is tjcafferkey on Git

2
Ma dal momento che puoi farlo in es6, probabilmente è un po 'eccessivo?
IonicBurger

1
Ciò consente di memorizzare la stringa in un posto in un formato e quindi in un secondo momento sostituire gli elementi corrispondenti utilizzando una sola funzione. A mia conoscenza, non è possibile farlo con i letterali modello es6. Un utilizzo per questo sarebbe, ad esempio, stringhe di traduzione in cui consumerai la stringa altrove e inserirai i valori desiderati lì.
Johan Persson

2

Ecco un altro modo per farlo utilizzando i letterali template es6 dinamicamente in fase di esecuzione.

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


1

Come rapido esempio:

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

L'output è:

jack ha 40 anni


7
È fantastico, a meno che tu non voglia fare qualcosa oltre a registrarlo sulla console.
alex

13
non dovrebbe essere console.log?
drzaus

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

Ho scritto un codice che ti consente di formattare facilmente la stringa.

Usa questa funzione.

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

Esempio

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

Produzione

Il mio nome è Mike e la mia età è 26

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.