Reputation: 309
I was trying to build a method that you take the first letter of every word and would capitalize it. I wrote it as
def titleize(name)
name.scan(/\w+/) { |x| x.capitalize! }
end
and it just wouldn't work properly. It wouldn't capitalize and letters. I did some searching and found the answer here Capitalizing titles eventually. It was written as
def titleize(name)
name.split(" ").each { |x| x.capitalize! }.join(" ")
end
How come my code didn't capitalize at all though? If I added a put statement and wrote it as
def titleize(name)
name.scan(/\w+/) { |x| puts x.capitalize! }
end
It would output "hi there" with capitals but the => would still be just "hi there" What did I miss?
Upvotes: 0
Views: 3169
Reputation: 118289
Corrected code:
def titleize(name)
name.scan(/\w+/).each { |x| x.capitalize! }.join(' ')
end
p titleize("ayan roy") #=>"Ayan Roy"
Let's see why your one not worked:
def titleize(name)
name.scan(/\w+/)
end
p titleize("ayan roy") #=>["ayan", "roy"]
Now your line name.scan(/\w+/) { |x| x.capitalize! }
, x
is passed as "ayan", "roy"
. Now look at the below:
def titleize(name)
name.scan(/\w+/) { |x| p x.capitalize! }
end
p titleize("ayan roy")
Output:
"Ayan"
"Roy"
"ayan roy"
As String#scan
says:
scan(pattern) {|match, ...| block } → str - if block is given,scan will return the
receiver
on which it is called. Both forms iterate through str, matching the pattern (which may be a Regexp or a String). For each match, a result is generated and either added to the result array or passed to the block.
Upvotes: 2
Reputation: 33646
Your code doesn't work because #scan
returns new String
objects which are the results of the Regexp and passes them to the block. So in your method you essentially took these new objects, mutated them by calling #capitalize!
but never used them anywhere afterwards.
You should do instead:
def titleize(name)
name.scan(/\w+/).each { |x| x.capitalize! }.join(' ')
end
But this seems more readable to me:
def titleize2(name)
name.split(' ').each { |w| w.capitalize! }.join(' ')
end
Note however these methods do not mutate the original argument passed.
Upvotes: 1
Reputation: 5490
The block form of scan
returns the original string, regardless of what you do in the block. (I think you may be able to alter the original string in the block by referring directly to it, but it's not recommended to alter the thing you're iterating over.) Instead, do your split
variation, but instead of each
, do collect
followed by join
:
name.split(" ").collect { |x| x.capitalize }.join(" ")
This works for titles containing numerals and punctuation, as well.
Upvotes: 0
Reputation: 20398
scan
returns/yields new strings and will never modify the source string. Perhaps you want gsub
.
def titleize(name)
name.gsub(/\w+/) {|x| x.capitalize }
end
Or perhaps better to use a likely more correct implementation from the titleize gem.
Upvotes: 2