Digamos, tengo una cadena para ser reemplazada:
let searches = ['gone', 'go', 'run'] let s = 'go went gone-go' const lookup = { 'go': '(go)', 'gone': '[gone]', } for (let key of searches) { s = s.replaceAll(key, lookup[key]) } console.log(s) Y obtengo (go) went [(go)ne]-(go) . Suponga que s puede ser cualquier cadena con algunas palabras de las claves de lookup , y los valores de lookup no necesariamente tendrán patrones consistentes. searches es la variable de entradas externas.
Si cambio los órdenes en las searches a, por ejemplo, ['go', 'gone', 'run'] , el resultado se convierte en (go) went (go)ne-(go) .
El resultado que espero es (go) went [gone]-(go) , de modo que los más largos se reemplazan primero y no serán reemplazados por coincidencias posteriores.
Se me ocurrió una solución que reemplazaba los valores de búsqueda a uuid primero, iterando de claves más largas a más cortas, luego reemplazaba uuid con los valores correspondientes. Por supuesto, esto es bastante estúpido e ineficiente:
let searches = ['go', 'gone', 'run'] let s = 'go went gone-go' const lookup = { 'go': '(go)', 'gone': '[gone]', } const uuid = () => Date.now().toString(36) + Math.random().toString(36).substring(2) // pseudo uuid for quick demo. src: https://stackoverflow.com/a/44078785/17954892 let uuidKeys = {} Object.keys(lookup).forEach(k => uuidKeys[k] = uuid()) // uuidKeys = {'go': 'random1', 'gone': 'random2'} let uuids = Object.values(uuidKeys) // uuids = ['random1', 'random2'] let uuidValues = {} Object.keys(lookup).forEach((k, i) => uuidValues[uuids[i]] = lookup[k]) // uuidValues = {'random1': '(go)', 'random2': '[gone]'} searches.sort((a, b) => b.length -a.length) // searches = ['gone', 'run', 'go'] for (let key of searches) { s = s.replaceAll(key, uuidKeys[key]) // s = 'random1 went random2-random1' } for (let key of searches.map(i => uuidKeys[i])) { s = s.replaceAll(key, uuidValues[key]) // s = '(go) went [gone]-(go)' } console.log(s)Luego pensé en dividir en bucle la cadena por búsquedas, luego reemplazar y registrar el índice que se procesa, y finalmente volver a unir la lista a la cadena. Sin embargo, no puedo encontrar una buena manera de implementarlo sin costosos métodos de matriz (plano, empalme, etc.) en bucles for.
¿Hay una forma elegante/eficiente de lograr el resultado?
Puede hacer esto usando una expresión regular con el indicador g con replace , pasando una función de devolución de llamada como reemplazo; Luego, la función elige el reemplazo apropiado en función de lo que coincida.
Por ejemplo:
let searches = ["gone", "go", "run"]; let s = "go went gone-go"; const lookup = { "go": "(go)", "gone": "[gone]", }; let rex = new RegExp(searches.map(escapeRegex).join("|"), "g"); s = s.replace(rex, match => lookup[match]); console.log(s); ...donde escapeRegex escapa a cualquier carácter en las cadenas de búsqueda que tengan un significado especial en las expresiones regulares; vea las respuestas de esta pregunta para posibles implementaciones.
Ejemplo en vivo:
function escapeRegex(string) { return string.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&'); } let searches = ["gone", "go", "run"]; let s = "go went gone-go"; const lookup = { "go": "(go)", "gone": "[gone]", }; let rex = new RegExp(searches.map(escapeRegex).join("|"), "g"); s = s.replace(rex, match => lookup[match]); console.log(s); // "(go) went [gone]-(go)" Nota: El orden de las cadenas en la matriz de searches es importante. Si pones "go" antes de "gone" , coincidirá primero:
function escapeRegex(string) { return string.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&'); } let searches = ["go", "gone", "run"]; // Note −−−−−−−−^ let s = "go went gone-go"; const lookup = { "go": "(go)", "gone": "[gone]", }; let rex = new RegExp(searches.map(escapeRegex).join("|"), "g"); s = s.replace(rex, match => lookup[match]); console.log(s); // "(go) went (go)ne-(go)"Si siempre desea que el más largo tenga la prioridad más alta y no puede controlar el contenido de la matriz de entrada, puede ordenarlo antes de usarlo:
function escapeRegex(string) { return string.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&'); } let searches = ["go", "gone", "run"]; // Note −−−−−−−−^ let s = "go went gone-go"; const lookup = { "go": "(go)", "gone": "[gone]", }; let rex = new RegExp( searches.sort((a, b) => b.length - a.length) .map(escapeRegex) .join("|"), "g" ); s = s.replace(rex, match => lookup[match]); console.log(s); // "(go) went [gone]-(go)"