How to replace the last occurrence of a substring in ruby?

0 votes
asked Jul 6, 2010 by ogrish-man

I want to replace the last occurrence of a substring in ruby. What's the most eastest way? For example, in abc123abc123, I want to replace the last abc to ABC. How can I did that?

6 Answers

0 votes
answered Jul 6, 2010 by chowlett

How about

new_str = old_str.reverse.sub(pattern.reverse, replacement.reverse).reverse

For instance:

irb(main):001:0> old_str = "abc123abc123"
=> "abc123abc123"
irb(main):002:0> pattern="abc"
=> "abc"
irb(main):003:0> replacement="ABC"
=> "ABC"
irb(main):004:0> new_str = old_str.reverse.sub(pattern.reverse, replacement.reverse).reverse
=> "abc123ABC123"
0 votes
answered Jul 6, 2010 by matsadler
string = "abc123abc123"
pattern = /abc/
replacement = "ABC"

matches = string.scan(pattern).length
index = 0
string.gsub(pattern) do |match|
  index += 1
  index == matches ? replacement : match
end
#=> abc123ABC123
0 votes
answered Jul 16, 2011 by daniel-pietzsch

Here's another possible solution:

>> s = "abc123abc123"
=> "abc123abc123"

>> s[s.rindex('abc')...(s.rindex('abc') + 'abc'.length)] = "ABC"
=> "ABC"

>> s
=> "abc123ABC123"
0 votes
answered Jul 29, 2013 by grosser

simple and efficient:

s = "abc123abc123abc"
p = "123"
s.slice!(s.rindex(p), p.size)
s == "abc123abcabc"
0 votes
answered Jul 28, 2016 by ribamar

When searching in huge streams of data, using reverse will definitively* lead to performance issues. I use string.rpartition*:

sub_or_pattern = "!"
replacement = "?"
string = "hello!hello!hello"

array_of_pieces = string.rpartition sub_or_pattern
( array_of_pieces[(array_of_pieces.find_index sub_or_pattern)] =  replacement ) rescue nil
p array_of_pieces.join
# "hello!hello?hello"

The same code must work with a string with no occurrences of sub_or_pattern:

string = "hello_hello_hello"
# ...
# "hello_hello_hello"

*rpartition uses rb_str_subseq() internally. I didn't check if that function returns a copy of the string, but I think it preserves the chunk of memory used by that part of the string. reverse uses rb_enc_cr_str_copy_for_substr(), which suggests that copies are done all the time -- although maybe in the future a smarter String class may be implemented (having a flag reversed set to true, and having all of its functions operating backwards when that is set), as of now, it is inefficient.

Moreover, Regex patterns can't be simply reverted. The question only asks for replacing the last occurrence of a sub-string, so, that's OK, but readers in the need of something more robust won't benefit from the most voted answer (as of this writing)

0 votes
answered Sep 15, 2017 by steve-craig

I've used this handy helper method quite a bit:

def gsub_last(str, source, target)
  return str unless str.include?(source)
  top, middle, bottom = str.rpartition(source)
  "#{top}#{target}#{bottom}"
end

If you want to make it more Rails-y, extend it on the String class itself:

class String
  def gsub_last(source, target)
    return self unless self.include?(source)
    top, middle, bottom = self.rpartition(source)
    "#{top}#{target}#{bottom}"
  end
end

Then you can just call it directly on any String instance, eg "fooBAR123BAR".gsub_last("BAR", "FOO") == "fooBAR123FOO"

Welcome to Q&A, where you can ask questions and receive answers from other members of the community.
Website Online Counter

...