In How do I limit the number of replacements when using gsub?, someone suggested the following way to do a limited number of substitutions:
str = 'aaaaaaaaaa'
count = 5
p str.gsub(/a/){if count.zero? then $& else count -= 1; 'x' end}
# => "xxxxxaaaaa"
It works, but the code mixes up how many times to substitute (5) with what the substitution should be ("x" if there should be a substitution, $&
otherwise). Is it possible to seperate the two out?
(If it's too hard to seperate the two things out in this scenario, but it can be done in some other scenarios, post that as an answer)
How about just extracting the replacement as an argument and encapsulating the counter by having the block close over it inside a method?
str = "aaaaaaaaaaaaaaa"
def replacements(replacement, limit)
count = limit
lambda { |original| if count.zero? then original else count -= 1; replacement end }
end
p str.gsub(/a/, &replacements("x", 5))
You can make it even more general by using a block for the replacement:
def limit(n, &block)
count = n
lambda do |original|
if count.zero? then original else count -= 1; block.call(original) end
end
end
Now you can do stuff like
p str.gsub(/a/, &limit(5) { "x" })
p str.gsub(/a/, &limit(5, &:upcase))
gsub
will call the block exactly as often as the regex matches the string. The only way to prevent that is to call break
in the block, however that will also keep gsub
from producing a meaningful return value.
So no, unless you call break in the block (which prevents any further code in the yielding method from running and thus prevents the method from returning anything), the number of times a method calls a block is solely determined by the method itself. So if you want gsub
to yield only 5 times, the only way to do that is to pass in a regex which only matches the given strings five times.
Why are you using gsub()
? By its design, gsub
is designed to replace all occurrences of something, so, right off the bat you're fighting it.
Use sub
instead:
str = 'aaaaaaaaaa'
count = 5
count.times { str.sub!(/a/, 'x') }
p str
# >> "xxxxxaaaaa"
str = 'mississippi'
2.times { str.sub!(/s/, '5') }
2.times { str.sub!(/s/, 'S') }
2.times { str.sub!(/i/, '1') }
p str
# >> "m1551SSippi"
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With