Búsqueda de sitios web

javascript - Reemplazar múltiples cadenas con otras múltiples cadenas


Estoy tratando de reemplazar múltiples palabras en una cadena con múltiples 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 múltiples cadenas por otras múltiples cadenas al mismo tiempo en JavaScript, de manera 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".

Respuesta #1

Solución específica

Puedes usar 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 quieres mantener dinámicamente la expresión regular y solo agregar intercambios futuros al mapa, puedes 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, solo podrías editar el mapa.

juguetear con expresiones regulares dinámicas

Haciéndolo reutilizable

Si quieres que esto sea un patrón general, podrías sacarlo a 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ías simplemente pasar la cadena y un mapa de los reemplazos que deseas a la función y devolvería la cadena transformada.

jugar con la función

Para asegurar que Object.keys funcione en navegadores antiguos, añade un polyfill, por ejemplo, de MDN o Es5.

Respuesta #2

Como respuesta a:

buscando una respuesta actualizada

Si estás usando "palabras" como en tu ejemplo actual, podrías ampliar la respuesta de Ben McCormick usando un grupo que no capture y agregar límites de palabra \b a la izquierda y a la derecha para prevenir coincidencias parciales.

\b(?:cathy|cat|catch)\b
  • \b Un límite de palabra para evitar una coincidencia parcial
  • (?: Grupo que no captura

    • cathy|cat|catch coincide con una de las alternativas
  • ) Cerrar grupo sin captura
  • \b Un límite de palabra para evitar una coincidencia parcial

Ejemplo para 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 para el ejemplo en los comentarios que parece que no está funcionando 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);

Respuesta #3

Usa elementos numerados para evitar reemplazar nuevamente. por ejemplo

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 siguen a un %. Los paréntesis capturan el número.

Este número (como una cadena) es el segundo parámetro, n, de la función lambda.

El +n-1 convierte la cadena en número y luego se resta 1 para indexar el arreglo de mascotas.

El %número se reemplaza luego con la cadena en el índice del arreglo.

La /g hace que la función lambda se llame repetidamente con cada número, que luego se reemplaza con una cadena del arreglo.

En JavaScript moderno:-

replace_n=(str,...ns)=>str.replace(/%(\d+)/g,(_,n)=>ns[n-1])

Respuesta #4

Esto puede no satisfacer tu necesidad exacta en este caso, pero he encontrado que esta es una forma útil de reemplazar múltiples parámetros en cadenas, como una solución general. Reemplazará todas las instancias de los parámetros, sin importar cuántas veces se hagan referencias 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'

Respuesta #5

usando Array.prototype.reduce():

RESPUESTA ACTUALIZADA (mucho mejor) (usando objeto): Esta función reemplazará todas las ocurrencias 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 un arreglo 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

Artículos relacionados