views:

50

answers:

1

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
  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!

+3  A: 

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
Chuck
Ah... *slaps forehead* obvious in hindsight. +10 for introducing me to Object#tap!
Stewart Johnson