Matthias
Matthias

Reputation: 4375

Ruby replace only exact matching string with another string

How can I solve following problem?

I have a html string like following one:

<p>aaa, b aa aaaaa?<br/>Next possible text b bb aa b.</p>

Now I'd like to replace for example only

"aaa" 

with

"<div class='special'>aaa</div>"

new string after replace:

<p><div class='special'>aaa</div>, b aa aaaaa?<br/>Next possible text b bb aa b.</p>

So I´d like a generic replacer, which only replace an exact matching string. "aaa" was just an example. It could also be "bb" or "two words" (=> two words, so text.split won't work in my opinion).

Anybody an idea for such an dynamic find, match and replacer?

I tried it already like following:

items = ["aaa", "a", "aa", "aa b", "c"]
text = "<p>aaa, b aa aaaaa?<br/>Next possible text b bb aa b.</p>"
words = text.split => ["<p>aaa,", "b", "aa", "aaaaa?<br/>Next", "possible", "text", "b", "bb", "aa", "b.</p>"]
new_words = []

words.each do |w|
   items.each do |item|
     if w == item
       w = '<div class="special">#{item}</div>'
     end
   end
  new_words << w
end
text = new_words.join(" ")

Result:

"<p>aaa, b <div class='special'>aa</div> aaaaa?<br/>Next possible text b bb <div class='special'>aa</div> b.</p>"

But it should be:

"<p><div class='special'>aaa</div>, b <div class='special'>aa</div> aaaaa?<br/>Next possible text b bb <div class='special'>aa b</div>.</p>"

My biggest problems are:

Someone an idea for fixing my problems?

EDIT: something is only a placeholder for my replacement.. my real replacement could also be:

%Q( <dfn title="#{strip_tags item.text}">#{item.name}</dfn> )

item.text could be everything => could contain also "aaa" item.name is for example "aaa"

So multiple gsub would replace also already replaced content.

Upvotes: 2

Views: 2331

Answers (4)

the Tin Man
the Tin Man

Reputation: 160551

It's not clear whether there is a single instance of aaa or multiple, and whether you want them all replaced, or just the first one.

This will replace just the first:

text = "<p>aaa, b aa aaaaa?<br/>Next possible text b bb aa b.</p>"
text.sub(/\b(aaa)\b/, %q"<div class='special'>\1</div>")
=> "<p><div class='special'>aaa</div>, b aa aaaaa?<br/>Next possible text b bb aa b.</p>"

This will replace all occurrences:

text = "<p>aaa, b aa aaaaa?<br/>Next possible text b bb aa b.</p>" * 2
=> "<p>aaa, b aa aaaaa?<br/>Next possible text b bb aa b.</p><p>aaa, b aa aaaaa?<br/>Next possible text b bb aa b.</p>"

text.gsub(/\b(aaa)\b/, %q"<div class='special'>\1</div>")
=> "<p><div class='special'>aaa</div>, b aa aaaaa?<br/>Next possible text b bb aa b.</p><p><div class='special'>aaa</div>, b aa aaaaa?<br/>Next possible text b bb aa b.</p>"

You can put "aaa" into a variable and find it by interpolating that target into the pattern:

target = 'aaa'
text.gsub(/\b(#{ target })\b/, %q"<div class='special'>\1</div>")
=> "<p><div class='special'>aaa</div>, b aa aaaaa?<br/>Next possible text b bb aa b.</p><p><div class='special'>aaa</div>, b aa aaaaa?<br/>Next possible text b bb aa b.</p>"

Regular expressions have the \b (word-break) marker, which makes it easy to define words, or substring, matches. You can replace "aaa" with multiple words also.

Upvotes: 3

fotanus
fotanus

Reputation: 20116

Here is the replacer method you wanted (using gsub, of course):

def replacer(orig,pattern,replace)
  orig.gsub(/#{pattern}(\s|\.|,)/,replace+'\1').to_s
end

2.0.0dev :001 > def replacer(orig,pattern,replace)
2.0.0dev :002?>     orig.gsub(/#{pattern}(\s|\.|,)/,replace+'\1').to_s
2.0.0dev :003?>   end
 => nil 
2.0.0dev :004 >  replacer("<p>aaa, b aa aaaaa?<br/>Next possible text b bb aa b.</p>", "aaa", "<div class='special'>aaa</div>")
 => "<p><div class='special'>aaa</div>, b aa aaaaa?<br/>Next possible text b bb aa b.</p>" 

Upvotes: -1

ian
ian

Reputation: 12251

You're looking for String#sub (not gsub)

s = "<p>aaa, b aa aaaaa?<br/>Next possible text b bb aa b.</p>"
# => "<p>aaa, b aa aaaaa?<br/>Next possible text b bb aa b.</p>"
match = "aaa"
# => "aaa"
replacement = "<div class='special'>aaa</div>"
# => "<div class='special'>aaa</div>"
s.sub match, replacement
# => "<p><div class='special'>aaa</div>, b aa aaaaa?<br/>Next possible text b bb aa b.</p>"
"<p><div class='special'>aaa</div>, b aa aaaaa?<br/>Next possible text b bb aa b.</p>" == _
# => true

Upvotes: 3

Dustin
Dustin

Reputation: 45

I would use a regular expression, Rubular is a great place to learn and test out your expressions. For more information on how to use gsub check out Jayfields Blog post. This may not fit all your use cases for this problem so you may need to modify it.

    str.gsub /^<p>a{3}/, "<div class='special'>aaa</div>"


   What this says is starting at the beginning of the string (^) 
   find <p> and 3 assurances of the letter a.

Upvotes: 1

Related Questions