Reemplace varias cadenas con varias otras cadenas
Estoy intentando reemplazar varias palabras en una cadena con varias otras palabras. La cadena es "Tengo un gato, un perro y una cabra".
Sin embargo, esto no produce "Tengo un perro, una cabra y un gato", sino que produce "Tengo un gato, un gato y un gato". ¿Es posible reemplazar varias cadenas con otras cadenas al mismo tiempo en JavaScript, de modo que se produzca el resultado correcto?
var str = "I have a cat, a dog, and a goat.";
str = str.replace(/cat/gi, "dog");
str = str.replace(/dog/gi, "goat");
str = str.replace(/goat/gi, "cat");
//this produces "I have a cat, a cat, and a cat"
//but I wanted to produce the string "I have a dog, a goat, and a cat".
Solución específica
Puede utilizar una función para reemplazar cada uno.
var str = "I have a cat, a dog, and a goat.";
var mapObj = {
cat:"dog",
dog:"goat",
goat:"cat"
};
str = str.replace(/cat|dog|goat/gi, function(matched){
return mapObj[matched];
});
ejemplo de jsfiddle
Generalizándolo
Si desea mantener dinámicamente la expresión regular y simplemente agregar intercambios futuros al mapa, puede hacer esto
new RegExp(Object.keys(mapObj).join("|"),"gi");
para generar la expresión regular. Entonces se vería así
var mapObj = {cat:"dog",dog:"goat",goat:"cat"};
var re = new RegExp(Object.keys(mapObj).join("|"),"gi");
str = str.replace(re, function(matched){
return mapObj[matched];
});
Y para agregar o cambiar más reemplazos, simplemente puede editar el mapa.
jugar con expresiones regulares dinámicas
Hacerlo reutilizable
Si desea que este sea un patrón general, puede extraerlo de una función como esta
function replaceAll(str,mapObj){
var re = new RegExp(Object.keys(mapObj).join("|"),"gi");
return str.replace(re, function(matched){
return mapObj[matched.toLowerCase()];
});
}
Entonces podría simplemente pasar la cadena y un mapa de los reemplazos que desea a la función y devolvería la cadena transformada.
jugar con la función
Para garantizar que Object.keys funcione en navegadores más antiguos, agregue un polyfill, por ejemplo, de MDN o Es5 .
Como respuesta a:
buscando una respuesta actualizada
Si está utilizando "palabras" como en el ejemplo actual, puede ampliar la respuesta de Ben McCormick utilizando un grupo sin captura y agregar límites de palabras \b
a la izquierda y a la derecha para evitar coincidencias parciales.
\b(?:cathy|cat|catch)\b
\b
Un límite de palabra para evitar una coincidencia parcial(?:
Grupo de no capturacathy|cat|catch
coincide con una de las alternativas
)
Cerrar grupo sin captura\b
Un límite de palabra para evitar una coincidencia parcial
Ejemplo de la pregunta original:
let str = "I have a cat, a dog, and a goat.";
const mapObj = {
cat: "dog",
dog: "goat",
goat: "cat"
};
str = str.replace(/\b(?:cat|dog|goat)\b/gi, matched => mapObj[matched]);
console.log(str);
Ejemplo del ejemplo en los comentarios que no parece funcionar bien:
let str = "I have a cat, a catch, and a cathy.";
const mapObj = {
cathy: "cat",
cat: "catch",
catch: "cathy"
};
str = str.replace(/\b(?:cathy|cat|catch)\b/gi, matched => mapObj[matched]);
console.log(str);
Utilice elementos numerados para evitar reemplazarlos nuevamente. p.ej
let str = "I have a %1, a %2, and a %3";
let pets = ["dog","cat", "goat"];
entonces
str.replace(/%(\d+)/g, (_, n) => pets[+n-1])
Cómo funciona: %\d+ encuentra los números que vienen después de %. Los corchetes capturan el número.
Este número (como una cadena) es el segundo parámetro, n, de la función lambda.
+n-1 convierte la cadena en un número y luego se resta 1 para indexar la matriz de mascotas.
Luego, el número % se reemplaza con la cadena en el índice de la matriz.
/g hace que la función lambda se llame repetidamente con cada número que luego se reemplaza con una cadena de la matriz.
En JavaScript moderno: -
replace_n=(str,...ns)=>str.replace(/%(\d+)/g,(_,n)=>ns[n-1])
Es posible que esto no satisfaga su necesidad exacta en este caso, pero he descubierto que es una forma útil de reemplazar múltiples parámetros en cadenas, como solución general. Reemplazará todas las instancias de los parámetros, sin importar cuántas veces se haga referencia a ellos:
String.prototype.fmt = function (hash) {
var string = this, key; for (key in hash) string = string.replace(new RegExp('\\{' + key + '\\}', 'gm'), hash[key]); return string
}
Lo invocarías de la siguiente manera:
var person = '{title} {first} {last}'.fmt({ title: 'Agent', first: 'Jack', last: 'Bauer' });
// person = 'Agent Jack Bauer'
usando Array.prototype.reduce() :
Respuesta ACTUALIZADA (mucho mejor) (usando objeto): esta función reemplazará todas las apariciones y no distingue entre mayúsculas y minúsculas
/**
* Replaces all occurrences of words in a sentence with new words.
* @function
* @param {string} sentence - The sentence to modify.
* @param {Object} wordsToReplace - An object containing words to be replaced as the keys and their replacements as the values.
* @returns {string} - The modified sentence.
*/
function replaceAll(sentence, wordsToReplace) {
return Object.keys(wordsToReplace).reduce(
(f, s, i) =>
`${f}`.replace(new RegExp(s, 'ig'), wordsToReplace[s]),
sentence
)
}
const americanEnglish = 'I popped the trunk of the car in a hurry and in a hurry I popped the trunk of the car'
const wordsToReplace = {
'popped': 'opened',
'trunk': 'boot',
'car': 'vehicle',
'hurry': 'rush'
}
const britishEnglish = replaceAll(americanEnglish, wordsToReplace)
console.log(britishEnglish)
// I opened the boot of the vehicle in a rush and in a rush I opened the boot of the vehicle
Respuesta ORIGINAL (usando una variedad de objetos):
const arrayOfObjects = [
{ plants: 'men' },
{ smart:'dumb' },
{ peace: 'war' }
]
const sentence = 'plants are smart'
arrayOfObjects.reduce(
(f, s) => `${f}`.replace(Object.keys(s)[0], s[Object.keys(s)[0]]), sentence
)
// as a reusable function
const replaceManyStr = (obj, sentence) => obj.reduce((f, s) => `${f}`.replace(Object.keys(s)[0], s[Object.keys(s)[0]]), sentence)
const result = replaceManyStr(arrayOfObjects , sentence1)
Ejemplo
// ///////////// 1. replacing using reduce and objects
// arrayOfObjects.reduce((f, s) => `${f}`.replace(Object.keys(s)[0], s[Object.keys(s)[0]]), sentence)
// replaces the key in object with its value if found in the sentence
// doesn't break if words aren't found
// Example
const arrayOfObjects = [
{ plants: 'men' },
{ smart:'dumb' },
{ peace: 'war' }
]
const sentence1 = 'plants are smart'
const result1 = arrayOfObjects.reduce((f, s) => `${f}`.replace(Object.keys(s)[0], s[Object.keys(s)[0]]), sentence1)
console.log(result1)
// result1:
// men are dumb
// Extra: string insertion python style with an array of words and indexes
// usage
// arrayOfWords.reduce((f, s, i) => `${f}`.replace(`{${i}}`, s), sentence)
// where arrayOfWords has words you want to insert in sentence
// Example
// replaces as many words in the sentence as are defined in the arrayOfWords
// use python type {0}, {1} etc notation
// five to replace
const sentence2 = '{0} is {1} and {2} are {3} every {5}'
// but four in array? doesn't break
const words2 = ['man','dumb','plants','smart']
// what happens ?
const result2 = words2.reduce((f, s, i) => `${f}`.replace(`{${i}}`, s), sentence2)
console.log(result2)
// result2:
// man is dumb and plants are smart every {5}
// replaces as many words as are defined in the array
// three to replace
const sentence3 = '{0} is {1} and {2}'
// but five in array
const words3 = ['man','dumb','plant','smart']
// what happens ? doesn't break
const result3 = words3.reduce((f, s, i) => `${f}`.replace(`{${i}}`, s), sentence3)
console.log(result3)
// result3:
// man is dumb and plants