Me gustaría saber cómo reemplazar un grupo de captura con su mayúscula en JavaScript. Aquí hay una versión simplificada de lo que he probado hasta ahora que no funciona:
> a="foobar"
'foobar'
> a.replace( /(f)/, "$1".toUpperCase() )
'foobar'
> a.replace( /(f)/, String.prototype.toUpperCase.apply("$1") )
'foobar'
¿Podría explicar qué está mal con este código?
javascript
regex
replace
uppercase
Evan Carroll
fuente
fuente
Respuestas:
Puede pasar una función a
replace
.var r = a.replace(/(f)/, function(v) { return v.toUpperCase(); });
Explicación
a.replace( /(f)/, "$1".toUpperCase())
En este ejemplo, pasa una cadena a la función de reemplazo. Dado que está utilizando la sintaxis de reemplazo especial ($ N toma la captura Nth) , simplemente está dando el mismo valor. En
toUpperCase
realidad, es engañoso porque solo está poniendo la cadena de reemplazo en mayúsculas (lo cual es algo inútil porque los caracteres$
y uno1
no tienen mayúsculas, por lo que el valor de retorno seguirá siendo"$1"
) .a.replace( /(f)/, String.prototype.toUpperCase.apply("$1"))
Lo crea o no, la semántica de esta expresión es exactamente la misma.
fuente
$1
al primer grupo de captura.Sé que llego tarde a la fiesta, pero aquí hay un método más corto que está más en la línea de sus intentos iniciales.
a.replace('f', String.call.bind(a.toUpperCase));
Entonces, ¿en qué te equivocaste y qué es este nuevo vudú?
Problema 1
Como se indicó anteriormente, estaba intentando pasar los resultados de un método llamado como el segundo parámetro de String.prototype.replace () , cuando en su lugar debería pasar una referencia a una función
Solución 1
Eso es bastante fácil de resolver. Simplemente eliminar los parámetros y los paréntesis nos dará una referencia en lugar de ejecutar la función.
a.replace('f', String.prototype.toUpperCase.apply)
Problema 2
Si intenta ejecutar el código ahora, obtendrá un error que indica que undefined no es una función y, por lo tanto, no se puede llamar. Esto se debe a que String.prototype.toUpperCase.apply es en realidad una referencia a Function.prototype.apply () a través de la herencia prototípica de JavaScript. Entonces, lo que estamos haciendo se parece más a esto
a.replace('f', Function.prototype.apply)
Que obviamente no es lo que pretendíamos. ¿Cómo sabe ejecutar Function.prototype.apply () en String.prototype.toUpperCase () ?
Solucion 2
Usando Function.prototype.bind () podemos crear una copia de Function.prototype.call con su contexto específicamente establecido en String.prototype.toUpperCase. Ahora tenemos lo siguiente
a.replace('f', Function.prototype.apply.bind(String.prototype.toUpperCase))
Problema 3
El último problema es que String.prototype.replace () pasará varios argumentos a su función de reemplazo. Sin embargo, Function.prototype.apply () espera que el segundo parámetro sea una matriz, pero en cambio obtiene una cadena o un número (dependiendo de si usa grupos de captura o no). Esto provocaría un error de lista de argumentos no válidos.
Solución 3
Afortunadamente, podemos simplemente sustituir Function.prototype.call () (que acepta cualquier número de argumentos, ninguno de los cuales tiene restricciones de tipo) por Function.prototype.apply () . ¡Hemos llegado al código de trabajo!
a.replace(/f/, Function.prototype.call.bind(String.prototype.toUpperCase))
¡Eliminando bytes!
Nadie quiere escribir un prototipo muchas veces. En su lugar, aprovecharemos el hecho de que tenemos objetos que hacen referencia a los mismos métodos a través de la herencia. El constructor String, al ser una función, hereda del prototipo de Function. Esto significa que podemos sustituir String.call por Function.prototype.call (en realidad, podemos usar Date.call para guardar aún más bytes, pero eso es menos semántico).
También podemos aprovechar nuestra variable 'a' ya que su prototipo incluye una referencia a String.prototype.toUpperCase , podemos intercambiar eso con a.toUpperCase. Es la combinación de las 3 soluciones anteriores y estas medidas de ahorro de bytes que es como obtenemos el código en la parte superior de esta publicación.
fuente
Publicación anterior, pero vale la pena extender la respuesta de @ChaosPandion para otros casos de uso con expresiones regulares más restringidas. Por ejemplo, asegúrese de que el
(f)
grupo de captura o el entorno envolvente con un formato específico/z(f)oo/
:> a="foobazfoobar" 'foobazfoobar' > a.replace(/z(f)oo/, function($0,$1) {return $0.replace($1, $1.toUpperCase());}) 'foobazFoobar' // Improve the RegEx so `(f)` will only get replaced when it begins with a dot or new line, etc.
Solo quiero resaltar los dos parámetros de
function
hace posible encontrar un formato específico y reemplazar un grupo de captura dentro del formato.fuente
return $0.replace($0, $1.toUpperCase())
, ¿dónde$0
está el primer argumento¿Por qué no buscamos la definición ?
Si escribimos:
a.replace(/(f)/, x => x.toUpperCase())
bien podríamos decir:
a.replace('f','F')
Peor aún, sospecho que nadie se da cuenta de que sus ejemplos han estado funcionando solo porque estaban capturando toda la expresión regular entre paréntesis. Si observa la definición , el primer parámetro que se pasa a la
replacer
función es en realidad el patrón coincidente completo y no el patrón que capturó entre paréntesis:function replacer(match, p1, p2, p3, offset, string)
Si desea utilizar la notación de función de flecha:
a.replace(/xxx(yyy)zzz/, (match, p1) => p1.toUpperCase()
fuente
SOLUCIÓN
a.replace(/(f)/,x=>x.toUpperCase())
para reemplazar todas las ocurrencias de grup, use
/(f)/g
regexp. El problema en su código:String.prototype.toUpperCase.apply("$1")
y"$1".toUpperCase()
da"$1"
(pruebe en la consola usted mismo) - por lo que no cambia nada y de hecho llama dos vecesa.replace( /(f)/, "$1")
(que tampoco cambia nada).Mostrar fragmento de código
let a= "foobar"; let b= a.replace(/(f)/,x=>x.toUpperCase()); let c= a.replace(/(o)/g,x=>x.toUpperCase()); console.log("/(f)/ ", b); console.log("/(o)/g", c);
fuente
Dado un diccionario (objeto, en este caso, a
Map
) de propiedad, valores y uso.bind()
como se describe en las respuestasconst regex = /([A-z0-9]+)/; const dictionary = new Map([["hello", 123]]); let str = "hello"; str = str.replace(regex, dictionary.get.bind(dictionary)); console.log(str);
Usando un objeto simple de JavaScript y con una función definida para obtener el valor de propiedad de retorno del objeto, o cadena original si no se encuentra
const regex = /([A-z0-9]+)/; const dictionary = { "hello": 123, [Symbol("dictionary")](prop) { return this[prop] || prop } }; let str = "hello"; str = str.replace(regex, dictionary[Object.getOwnPropertySymbols(dictionary)[0]].bind(dictionary)); console.log(str);
fuente