why doesn't my refactored ruby using inject work?
I tried to do some refactoring to convert an each block into an inject, but it didn't work and I don't understand why.
Here's the code that works before refactoring:
class String
# Build the word profile for the given word. The word profile is an array of
# 26 integers -- each integer is a count of the number of times each letter
# appears in the word.
#
def profile
profile = Array.new(26) { 0 }
self.downcase.split(//).each do |letter|
# only process letters a-z
profile[letter.ord - 'a'.ord] += 1 unless letter.ord > 'z'.ord
end
profile开发者_StackOverflow
end
end
and here's my refactor that doesn't work:
class String
# Build the word profile for the given word. The word profile is an array of
# 26 integers -- each integer is a count of the number of times each letter
# appears in the word.
#
def profile
self.downcase.split(//).inject(Array.new(26) {0}) do |profile, letter|
# only process letters a-z
profile[letter.ord - 'a'.ord] += 1 unless letter.ord > 'z'.ord
end
end
end
When I try and execute the refactored method I'm getting
`block in profile': undefined method `[]=' for 1:Fixnum (NoMethodError)
If I understand that correctly, it's doesn't like the array reference operator on the profile object in my refactored version, which implies that the initialiser passed to inject isn't working. Is that understanding correct? And if so, why not?
Thanks!
The []=
method returns the assigned value, so the value of profile
in the next iteration will be 1 (since it's the value of the last iteration). In order to get the behavior you want, you'll have to do:
self.downcase.split(//).inject(Array.new(26) {0}) do |profile, letter|
# only process letters a-z
profile[letter.ord - 'a'.ord] += 1 unless letter.ord > 'z'.ord
profile
end
or
self.downcase.split(//).inject(Array.new(26) {0}) do |profile, letter|
# only process letters a-z
profile.tap { profile[letter.ord - 'a'.ord] += 1 unless letter.ord > 'z'.ord }
end
精彩评论