Passing blocks to gsub, (OF COURSE Ruby does it this way!)

by Derek Sivers

I love that when Ruby surprises me, it's never the kind of surprise like, "Huh? Why does it do that?" - but instead it's always, "Oh, wow, OF COURSE that makes perfect sense, I just never thought of it like that!"

Newest case of that: passing blocks to gsub

I had always done gsub using a string as the replacement:

> s = 'pretty little ponies'
> s.gsub(/[^aeiou]/, '_')
=> "__e_____i___e__o_ie_"

But the book Ruby for Rails had an example of passing a block into gsub:

> s.gsub(/[^aeiou]/) {|c| c.upcase}
=> "PReTTY LiTTLe PoNieS"

Beautiful

7 Comments

SomeGuy
2006-06-02 13:28:40
Very cool. I'm a PHP fan who is looking at doing his first RoR project. I'm wondering how this is going for you? Still think it's a "Good Thing"?
Daddy Bob
2006-06-27 18:46:58
The internet BABY is soon gonna have to face a giant-in the real world-not the fantasy-safe internet world.
Are you ready Sivers?
Henri
2006-06-27 19:13:06
I just discovered Ruby over the weekend and I was experimenting a bit. How about this:


temparray = resultstring.split(" ") # split the string into an array
temparray.collect! { |value| value.sub(/[A-Za-z0-9]+/) {|s| s.reverse }}
resultstring = temparray.join(" ") # join the array back into a string
puts "resultstring words reversed: " + resultstring


I takes a sentence, breaks it down to individual words, reverses the words excluding punctuation marks and assembles the result back into a sentence. To experienced ruby users it may seem nothing, but to me it was quite an eye opener to ruby.

Robin
2007-09-11 15:59:21
@Henri,


Even more fun to pack it all in one line:


resultstring.split.collect { |v| v.sub(/\w+/) { |s| s.reverse } }.join(' ')

Robin
2007-09-11 16:04:22
Actually, am I crazy or is it really just:


resultstring.gsub(/\w+/) { |s| s.reverse }

Voloshin Ruslan
2007-10-17 02:57:04
For generate correct logins
login.to_s.downcase.gsub(/[^\._\-a-z0-9\@]/i,'_')
Tim
2008-01-30 17:00:19
Thanks for the post. It helped me figure out the ruby version of a one line perl script I like to run to hi-light regular expression hits within a text. "91" can be replaced by various numbers--though not all numbers work.


ruby -ne 'puts $_.gsub(/regex/i){|match| match = "\e[91m#{match}\e[m"}' < textfile.txt


Only the matched lines can be output--sort of a grep with hi-lighting--with the following:


ruby -ne 'puts $_.gsub(/regex/i){|match| match = "\e[93m#{match}\e[m"} if $_.match(/regex/i)' < textfile.txt


I wonder if there isn't a better way though, since this requires two searches per line of input. Perhaps gsub sets a variable when a match is found. That variable might be the best way to decide whether the line should be printed or not because it would require only one regex search rather than two.