How to limit the number of times a block is called?

Q How to limit the number of replacements when using gsub? , someone suggested the following way to make a limited number of substitutions:

str = 'aaaaaaaaaa' count = 5 p str.gsub(/a/){if count.zero? then $& else count -= 1; 'x' end} # => "xxxxxaaaaa" 

This works, but the code mixes how many times to replace (5) with what should be a substitution ("x" if there should be a substitution, $& otherwise). Can two of them be separated?

(If it is too difficult to separate two things in this scenario, but it can be done in some other scenarios, post this as an answer)

+8
ruby block
source share
3 answers

How to simply extract the replacement as an argument and encapsulate the counter by blocking the block above it inside the 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 this even more general by using a replacement block:

 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 things like

 p str.gsub(/a/, &limit(5) { "x" }) p str.gsub(/a/, &limit(5, &:upcase)) 
+8
source share

gsub will call the block just as often as the regular expression matches the string. The only way to prevent this is to call break in the block, however it will also save gsub from creating a meaningful return value.

Thus, no, unless you cause a gap in the block (which prevents the execution of any additional code in the yielding method and thus prevents the method from returning anything), the number of times the method calls the block is determined only by the method itself. Therefore, if you want gsub give only 5 times, the only way to do this is to pass into a regular expression that only matches these lines five times.

+2
source share

Why are you using gsub() ? By its design, gsub designed to replace all occurrences of something, so from the very beginning you fight with 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" 
+1
source share

All Articles