¿Dar formato a una cadena de JavaScript utilizando marcadores de posición y un objeto de sustituciones?

92

Tengo una cadena con decir: My Name is %NAME% and my age is %AGE%.

%XXX%son marcadores de posición. Necesitamos sustituir valores allí de un objeto.

El objeto se parece a: {"%NAME%":"Mike","%AGE%":"26","%EVENT%":"20"}

Necesito analizar el objeto y reemplazar la cadena con los valores correspondientes. Entonces ese resultado final será:

Mi nombre es Mike y mi edad es 26.

Todo debe hacerse usando javascript puro o jquery.

Joby Joseph
fuente
2
Eso se parece más a un objeto que a una matriz
Joel Coehoorn
3
¿Qué has intentado hasta ahora? ¿Ha mirado el método string .replace () ? (Además, no tiene una matriz allí, tiene un objeto.)
nnnnnn
1
Eso es bastante feo. ¿Seguramente estarías igual de bien servido por {NAME: "Mike", AGE: 26, EVENT: 20}? Por supuesto, aún necesitaría que estas teclas aparezcan con signos de porcentaje en la cadena de entrada.
Davidchambers

Respuestas:

151

Los requisitos de la pregunta original claramente no podrían beneficiarse de la interpolación de cadenas, ya que parece que es un procesamiento en tiempo de ejecución de claves de reemplazo arbitrarias.

Sin embargo , si solo tuviera que hacer una interpolación de cadenas, puede usar:

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

Tenga en cuenta las comillas inversas que delimitan la cadena, son obligatorias.


Para obtener una respuesta que se adapte a los requisitos del OP en particular, puede usar String.prototype.replace()para los reemplazos.

El siguiente código manejará todas las coincidencias y no las tocará sin un reemplazo (siempre que sus valores de reemplazo sean todas cadenas, si no, vea a continuación).

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 algunos de sus reemplazos no son cadenas, asegúrese de que existan primero en el objeto. Si tiene un formato como el del ejemplo, es decir, envuelto en signos de porcentaje, puede usar el inoperador para lograrlo.

jsFiddle .

Sin embargo, si su formato no tiene un formato especial, es decir, ninguna cadena, y su objeto de reemplazo no tiene un nullprototipo, utilice Object.prototype.hasOwnProperty(), a menos que pueda garantizar que ninguna de sus posibles subcadenas reemplazadas chocará con los nombres de propiedad en el prototipo.

jsFiddle .

De lo contrario, si su cadena de reemplazo fuera 'hasOwnProperty', obtendría una cadena desordenada resultante.

jsFiddle .


Como nota al margen, debería llamarse replacementsun Object, no un Array.

alex
fuente
2
+1. Agradable. Aunque es posible que desee decir return replacements[all] || allque cubra los %NotInReplacementsList%casos.
nnnnnn
6
Esto no funcionará si el valor de reemplazo es falso. Así que es mejor usar esta declaración de devolución:return all in params ? params[all] : all;
Michael Härtl
@ MichaelHärtl ¿No deberían todos sus reemplazos ser cuerdas? Si desea reemplazar con una cadena vacía, es mejor verificar con otros medios.
alex
1
@alex Tuve la situación en la que los reemplazos también podían ser enteros, e incluso 0. En este caso no funcionó.
Michael Härtl
@ MichaelHärtl Actualizado para cubrir ese caso.
alex
24

¿Qué tal el uso de literales de plantilla ES6?

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

Más sobre las plantillas literales ...

Tomasz Mularczyk
fuente
5
Si tiene un objeto con marcadores de posición como el OP, ¿cómo ayuda la interpolación de cadenas?
alex
¡Funciona de maravilla! La solución más pragmática a mis problemas de mantenimiento de lugar, perfecta.
BAERUS
Esta solución no funciona para reemplazos en tiempo de ejecución
Thierry J.
13

Puede usar JQuery (jquery.validate.js) para que funcione fácilmente.

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

O si desea usar solo esa función, puede definir esa función y usarla 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 al equipo de jquery.validate.js

zawhtut
fuente
1
Definitivamente no querrás cargar este complemento solo por esto, pero ya lo estoy usando para validar un formulario en la página ... ¡así que gracias por el consejo!
nabrown
1
Bastante ineficiente para crear una expresión regular para cada número, sería mejor hacer coincidir todos los números y luego reemplazar si el valor se encuentra en la matriz, ¿quizás?
alex
para los interesados, sucede aquí: github.com/jquery-validation/jquery-validation/blob/master/src/…
Raphael C
1
+ muy agradable ... y para $.eachque 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;}puedas hacer que no tengas que incluir jquery solo para ese;)
Larphoid
11

Al igual que con los navegadores modernos, marcador de posición es apoyada por la nueva versión de Chrome / Firefox, similar a la función de estilo C printf().

Marcadores de posición:

  • %s Cuerda.
  • %d, %iNúmero entero.
  • %f Número de coma flotante.
  • %o Hipervínculo de objeto.

p.ej

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

Por cierto, para ver la salida:

  • En Chrome, use el acceso directo Ctrl + Shift + JoF12 para abrir la herramienta de desarrollo.
  • En Firefox, use el acceso directo Ctrl + Shift + Ko F12para abrir la herramienta de desarrollo.

@Update - soporte de nodejs

Parece que nodejs no es compatible %f, en cambio, podría usarse %den nodejs. Con %dnúmero se imprimirá como número flotante, no solo como entero.

Eric Wang
fuente
6

Solo usa replace()

var values = {"%NAME%":"Mike","%AGE%":"26","%EVENT%":"20"};
var substitutedString = "My Name is %NAME% and my age is %AGE%.".replace("%NAME%", $values["%NAME%"]).replace("%AGE%", $values["%AGE%"]);
hafichuk
fuente
3
obligatorio "utilice MDN como referencia": developer.mozilla.org/en/JavaScript/Reference/Global_Objects/…
mu es demasiado corto
2
Gracias por los chicos de azotes - apreciado;)
hafichuk
5

Puede usar una función de reemplazo 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);

Puedes verlo funcionar aquí: http://jsfiddle.net/jfriend00/DyCwk/ .

jfriend00
fuente
1
Genial, Alex hizo casi exactamente lo mismo pero en menos líneas de código (aunque los operadores ternarios son probablemente más lentos que si ... de lo contrario).
RobG
¡Oye, te di un +1! Ambos hicieron una función de reemplazo, la suya no es exactamente la misma pero bastante similar. Su RegExp también es diferente, el OP estaría mejor usando %% o $$ o similar como delimitadores; es probable que un solo% o% ocurra en una cadena normalmente, pero es poco probable que se dupliquen.
RobG
4

Si quieres hacer algo más cercano a console.log como reemplazar% s marcadores de posición como en

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

yo escribí esto

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

conseguirás

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

ACTUALIZAR

He agregado una variante simple tan String.prototypeútil cuando se trata de transformaciones de cadenas, aquí 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;
   }

En ese caso lo harás

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

Prueba esta versión aquí

Loretoparisi
fuente
2
Hice una variación en su función sin prototipos, formatMessage(message: string, values: string[]) { let i = 0; return message.replace(/%\w+%/g, (match, idx) => { return values[i++]; }); } esto toma un mensaje para formatear y una matriz de valores de reemplazo y busca%SOME_VALUE%
baku
4

Actualmente todavía no existe una solución nativa en Javascript para este comportamiento. Las plantillas etiquetadas son algo relacionado, pero no lo resuelvas.

Aquí hay un refactor de la solución de Alex con un objeto para reemplazos.

La solución utiliza funciones de flecha y una sintaxis similar para los marcadores de posición como la interpolación nativa de Javascript en literales de plantilla (en {}lugar de %%). Además, no es necesario incluir delimitadores (% ) en los nombres de los reemplazos.

Hay dos sabores: descriptivo y reducido.

Solución descriptiva:

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

Solución reducida:

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

AMS777
fuente
1
Gracias por esto, es genial tener una solución genérica para una cadena de marcador de posición y un objeto para fusionarla de esta manera
Carlos P
@CarlosP, esa es la idea, tener una solución genérica. Pero creo que debería ser algo nativo del idioma, ya que es un caso de uso común.
AMS777
1
Esto se puede simplificar un poco más usando grupos de expresiones regulares como este:stringWithPlaceholders.replace(/{(\w+)}/g, (fullMatch, group1) => replacements[group1] || fullMatch )
Kade hace
2

Esto te permite hacer exactamente eso

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

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

Haciendo lo siguiente:

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

// My username is tjcafferkey on Git
tjcafferkey
fuente
2
Pero dado que puedes hacerlo en es6, ¿probablemente sea un poco exagerado?
IonicBurger
1
Esto le permite almacenar la cadena en un lugar en un formato y luego, en un momento posterior, reemplazar los elementos correspondientes utilizando solo una función. Que yo sepa, eso no es posible con los literales de plantilla es6. Un uso para esto sería, por ejemplo, cadenas de traducción donde consumirá la cadena en otro lugar e inyectará los valores deseados allí.
Johan Persson
2

Aquí hay otra forma de hacer esto mediante el uso de literales de plantilla es6 dinámicamente en tiempo de ejecución.

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

Simon Schärer
fuente
1

Como ejemplo rápido:

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

La salida es:

Jack tiene 40 años

Yomi
fuente
7
Eso es genial, a menos que quieras hacer algo además de registrarlo en la consola.
alex
13
¿No debería ser eso 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?"
Shubham Vyas
fuente
0

He escrito un código que te permite formatear cadenas fácilmente.

Utilice esta función.

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

Ejemplo

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

Salida

Mi nombre es Mike y mi edad es 26

bikram
fuente