Mettre en forme une chaîne JavaScript en utilisant des espaces réservés et un objet de substitutions?


92

J'ai une chaîne avec dire: My Name is %NAME% and my age is %AGE%.

%XXX%sont des espaces réservés. Nous devons y substituer des valeurs à partir d'un objet.

L'objet ressemble à: {"%NAME%":"Mike","%AGE%":"26","%EVENT%":"20"}

J'ai besoin d'analyser l'objet et de remplacer la chaîne par les valeurs correspondantes. Donc, la sortie finale sera:

Je m'appelle Mike et j'ai 26 ans.

Le tout doit être fait en utilisant javascript pur ou jquery.


2
Cela ressemble plus à un objet qu'à un tableau
Joel Coehoorn

3
Qu'avez-vous essayé jusqu'à présent? Avez-vous regardé la méthode string .replace () ? (De plus, vous n'avez pas de tableau ici, vous avez un objet.)
nnnnnn

1
C'est assez moche. Vous seriez sûrement aussi bien servis {NAME: "Mike", AGE: 26, EVENT: 20}? Vous exigeriez toujours que ces clés apparaissent réservées par des signes de pourcentage dans la chaîne d'entrée, bien sûr.
davidchambers

Réponses:


152

Les exigences de la question d'origine ne pouvaient clairement pas bénéficier de l'interpolation de chaîne, car il semble qu'il s'agisse d'un traitement d'exécution de clés de remplacement arbitraires.

Cependant , si vous deviez juste faire une interpolation de chaîne, vous pouvez utiliser:

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

Notez les backticks délimitant la chaîne, ils sont obligatoires.


Pour une réponse adaptée à l'exigence du PO particulier, vous pouvez utiliser String.prototype.replace()pour les remplacements.

Le code suivant gérera toutes les correspondances et ne touchera pas celles sans remplacement (à condition que vos valeurs de remplacement soient toutes des chaînes, sinon, voir ci-dessous).

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 .

Si certains de vos remplacements ne sont pas des chaînes, assurez-vous qu'ils existent d'abord dans l'objet. Si vous avez un format comme l'exemple, c'est-à-dire entouré de signes de pourcentage, vous pouvez utiliser l' inopérateur pour y parvenir.

jsFiddle .

Cependant, si votre format n'a pas de format spécial, c'est-à-dire aucune chaîne, et que votre objet de remplacement n'a pas de nullprototype, utilisez Object.prototype.hasOwnProperty(), à moins que vous ne puissiez garantir qu'aucune de vos sous-chaînes remplacées potentielles n'entrera en conflit avec les noms de propriétés sur le prototype.

jsFiddle .

Sinon, si votre chaîne de remplacement l'était 'hasOwnProperty', vous obtiendrez une chaîne résultante en désordre.

jsFiddle .


En passant, vous devriez être appelé replacementsun Object, pas un Array.


2
+1. Agréable. Bien que vous souhaitiez peut-être dire return replacements[all] || allpour couvrir les %NotInReplacementsList%cas.
nnnnnn

6
Cela ne fonctionnera pas si la valeur de remplacement est fausse. Il est donc préférable d'utiliser cette déclaration de retour:return all in params ? params[all] : all;
Michael Härtl

@ MichaelHärtl Vos remplaçants ne devraient-ils pas tous être des chaînes? Si vous souhaitez remplacer par une chaîne vide, mieux vaut vérifier avec d'autres moyens.
alex

1
@alex J'ai eu la situation où les remplacements pouvaient aussi être des entiers, et même 0. Dans ce cas, cela n'a pas fonctionné.
Michael Härtl

@ MichaelHärtl Mise à jour pour couvrir cette affaire.
alex

24

Que diriez-vous d'utiliser des littéraux de modèle ES6?

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

En savoir plus sur les littéraux de modèle ...


5
Si vous avez un objet avec des espaces réservés comme l'OP, comment l'interpolation de chaîne aide-t-elle cela?
alex

Fonctionne comme un charme! La solution la plus pragmatique à mes problèmes de placeholding, parfaite.
BAERUS

Cette solution ne fonctionne pas pour les remplacements d'exécution
Thierry J.

13

Vous pouvez utiliser JQuery (jquery.validate.js) pour le faire fonctionner facilement.

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

Ou si vous souhaitez utiliser uniquement cette fonctionnalité, vous pouvez définir cette fonction et l'utiliser simplement comme

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édit à l'équipe jquery.validate.js


1
Vous ne voudriez certainement pas charger ce plugin juste pour cela, mais je l'utilise déjà pour valider un formulaire sur la page ... alors merci pour le conseil!
nabrown

1
Assez inefficace pour créer une regex pour chaque nombre, il serait préférable de faire correspondre tous les nombres, puis de remplacer si la valeur a été trouvée dans le tableau, peut-être?
alex le

pour ceux qui sont intéressés, cela se passe ici: github.com/jquery-validation/jquery-validation/blob/master/src/…
Raphael C

1
+ très sympa ... et $.eachvous pourriez faire en 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;}sorte que vous n'ayez pas à inclure jquery juste pour celui-là;)
Larphoid

11

Comme avec le navigateur moderne, espace réservé est pris en charge par une nouvelle version de Chrome / Firefox, similaire à la fonction de style C printf().

Espaces réservés:

  • %s Chaîne.
  • %d, %iNombre entier.
  • %f Nombre à virgule flottante.
  • %o Lien hypertexte de l'objet.

par exemple

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

BTW, pour voir la sortie:

  • Dans Chrome, utilisez un raccourci Ctrl + Shift + Jou F12pour ouvrir l'outil de développement.
  • Dans Firefox, utilisez le raccourci Ctrl + Shift + Kou F12pour ouvrir l'outil de développement.

@Update - Prise en charge de nodejs

Semble que nodejs ne prend pas en charge %f, à la place, pourrait être utilisé %ddans nodejs. Avec %dnombre sera imprimé comme un nombre flottant, pas seulement un entier.



5

Vous pouvez utiliser une fonction de remplacement personnalisée comme celle-ci:

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

Vous pouvez le voir fonctionner ici: http://jsfiddle.net/jfriend00/DyCwk/ .


1
Cool, Alex a fait à peu près exactement la même chose mais en moins de lignes de code (bien que les opérateurs ternaires soient probablement plus lents que if..else).
RobG

Hé, je t'ai donné un +1! Vous avez tous les deux fait une fonction de remplacement, la vôtre n'est pas exactement la même mais assez similaire. Votre RegExp est également différent, l'OP serait mieux d'utiliser %% ou $$ ou similaire comme délimiteurs - un seul% ou% est susceptible de se produire dans une chaîne normalement, mais les doubles sont peu probables.
RobG

4

Si vous voulez faire quelque chose de plus proche de console.log, comme remplacer les espaces réservés% s comme dans

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

J'ai écrit ceci

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

tu auras

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

MISE À JOUR

J'ai ajouté une variante simple aussi String.prototypeutile pour traiter les transformations de chaînes, la voici:

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

Dans ce cas, vous ferez

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

Essayez cette version ici


2
J'ai fait une variation de votre fonction sans prototypes, formatMessage(message: string, values: string[]) { let i = 0; return message.replace(/%\w+%/g, (match, idx) => { return values[i++]; }); } cela prend un message au format et un tableau de valeurs de remplacement et recherche%SOME_VALUE%
baku

4

Actuellement, il n'y a toujours pas de solution native en Javascript pour ce comportement. Les modèles balisés sont liés, mais ne le résolvent pas.

Ici, il y a un refactor de la solution d' Alex avec un objet pour les remplacements.

La solution utilise des fonctions fléchées et une syntaxe similaire pour les espaces réservés comme l' interpolation Javascript native dans les littéraux de modèle ( {}au lieu de %%). Il n'est pas non plus nécessaire d'inclure des délimiteurs ( %) dans les noms des remplacements.

Il existe deux saveurs: descriptive et réduite.

Solution descriptive:

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

Solution réduite:

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
Merci pour cela, c'est formidable d'avoir une solution générique pour une chaîne d'espace réservé et un objet à fusionner comme ceci
Carlos P

@CarlosP, c'est l'idée, d'avoir une solution générique. Mais je pense que cela devrait être quelque chose de natif de la langue, car c'est un cas d'utilisation courant.
AMS777

1
Cela peut être simplifié un peu plus en utilisant des groupes de regex comme celui-ci:stringWithPlaceholders.replace(/{(\w+)}/g, (fullMatch, group1) => replacements[group1] || fullMatch )
Kade

2

Cela vous permet de faire exactement cela

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

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

En procédant comme suit:

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

// My username is tjcafferkey on Git

2
Mais puisque vous pouvez le faire dans es6, c'est probablement un peu exagéré?
IonicBurger

1
Cela vous permet de stocker la chaîne en un seul endroit dans un format, puis de remplacer ultérieurement les éléments correspondants en utilisant une seule fonction. À ma connaissance, il n'est pas possible de faire avec les littéraux de modèle es6. Une utilisation pour cela serait par exemple des chaînes de traduction où vous utiliserez la chaîne ailleurs et y injecterez les valeurs souhaitées.
Johan Persson

2

Voici une autre façon de faire cela en utilisant dynamiquement les littéraux de modèle es6 au moment de l'exécution.

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

À titre d'exemple rapide:

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

La sortie est:

Jack a 40 ans


7
C'est génial, à moins que vous ne souhaitiez faire autre chose que de le connecter à la console.
alex le

13
cela ne devrait-il pas être 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

J'ai écrit un code qui vous permet de formater facilement la chaîne.

Utilisez cette fonction.

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

Exemple

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

Production

Je m'appelle Mike et j'ai 26 ans

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.