Tengo un fragmento de código donde puedo cambiar palabras de @post.swap_content a hipervínculos por palabra clave. Por ejemplo, tengo una palabra 'michigan' en @post.swap_content y tengo la palabra clave 'Michigan' en palabras clave, por lo que cambiaría al hipervínculo adjunto a la palabra clave. Aquí está parte de la función:
def execute all_keys = Keyword.all.pluck(:key, :link).to_h.transform_keys(&:downcase) @post.swap_content = @post.swap_content.to_s.gsub!(/\w+/) do |word| url = all_keys[word.downcase] url ? "<a href='#{url}'>#{word}</a>" : word end @post.save! endY mi pregunta es: ¿cómo puedo hacer que gsub solo las dos primeras palabras clave en @post.swap_content? Por ejemplo, tengo @post.swap_content 'michigan, michigan y michigan, utah y utah', ¿cómo puedo cambiar a hipervínculos solo con las dos primeras palabras clave (las dos primeras 'michigan' y las dos primeras 'utah')? Creo que de alguna manera necesito trabajar con gsub, pero no sé cómo manejar la cantidad de palabras que pueden ser gsub.
Puede proporcionar un bloque a gsub que se invocará con cada coincidencia, puede usar esto para contar las ocurrencias y reemplazar el contenido de forma condicional.
str = "Dog dog dog cat cat cat" occurences = {} str.gsub(/\w+/) do |match| # downcase so Dog and dog are counted together key = match.downcase # build a hash which counts the number of times we've matched a word. count = occurences.store(key, occurences.fetch(key, 0).next) # return the word unchanged or wrap in a hyperlink depending on count count > 2 ? match : "<a>#{match}</a>" end # output => "<a>Dog</a> <a>dog</a> dog <a>cat</a> <a>cat</a> cat"Suponer:
str = "Dog dog cat dog cat Dog cat cat"Si el motor de expresiones regulares de Ruby admitiera la búsqueda negativa de longitud variable, podríamos escribir:
R = /\b(\w+)\b(?<!(?:\b\1\b.*){2})/i str.gsub(R, '<a>\1</a>') #=> "<a>Dog</a> <a>dog</a> <a>cat</a> dog <a>cat</a> Dog cat cat"Podemos escribir esta expresión regular en modo de espacio libre para que sea autodocumentada:
R = / \b # assert a word break (\w+) # match 1+ word characters and save to capture group 1 \b # assert a word break (?! # begin a negative lookbehind (?: # begin a non-capture group \b # assert a word break \1 # match the content of capture group 1 \b # assert a word break .* # match 0+ characters ) # end non-capture group {2} # execute non-capture group twice ) # end negative lookbehind /ix # assert case-independent and free-spacing regex def modes Desafortunadamente, el motor de expresiones regulares de Ruby no admite búsquedas retrospectivas de longitud variable (positivas o negativas) (aunque algún día podría). Sin embargo, admite búsquedas anticipadas de longitud variable (positivas y negativas). Por lo tanto, podríamos invertir la cadena, realizar los reemplazos deseados usando gsub y luego invertir la cadena resultante, de la siguiente manera:
R = /\b(\w+)\b(?!(?:.*\b\1\b){2})/i str.reverse.gsub(R, '>a/<\1>a<').reverse #=> "<a>Dog</a> <a>dog</a> <a>cat</a> dog <a>cat</a> Dog cat cat"Los pasos son los siguientes.
s = str.reverse #=> "tac tac goD tac god tac god goD" t = s.gsub(R, '>a/<\1>a<') #=> "tac tac goD >a/<tac>a< god >a/<tac>a< >a/<god>a< >a/<goD>a<" t.reverse #=> "<a>Dog</a> <a>dog</a> <a>cat</a> dog <a>cat</a> Dog cat cat"Echemos un vistazo más de cerca a la expresión regular.
R = / \b # assert a word break (\w+) # match 1+ word characters and save to capture group 1 \b # assert a word break (?! # begin a negative lookahead (?: # begin a non-capture group .* # match 0+ characters \b # assert a word break \1 # match the content of capture group 1 \b # assert a word break ) # end non-capture group {2} # execute non-capture group twice ) # end negative lookahead /ix # assert case-independent and free-spacing regex def modes