Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

TypeError: can't convert String into Integer

Tags:

ruby

I have code:

class Scene
  def initialize(number)
    @number = number
  end
  attr_reader :number
end

scenes = [Scene.new("one"), Scene.new("one"), Scene.new("two"), Scene.new("one")]

groups = scenes.inject({}) do |new_hash, scene|
   new_hash[scene.number] = [] if new_hash[scene.number].nil?
   new_hash[scene.number] << scene
end

When I'm lauching it I get error:

freq.rb:11:in `[]': can't convert String into Integer (TypeError)
       from freq.rb:11:in `block in <main>'
       from freq.rb:10:in `each'
       from freq.rb:10:in `inject'
       from freq.rb:10:in `<main>'

If I change scenes to:

scenes = [Scene.new(1), Scene.new(1), Scene.new(2), Scene.new(1)]

the problem dissapear.

Why I get error message in the first case? Why Ruby decide to convert scene.number from String to Integer?

And one additional question about the 'inject' method. When Ruby initialize the 'new_hash' variable and how can Ruby know the type of this variable?

like image 953
ceth Avatar asked Mar 21 '10 09:03

ceth


2 Answers

try:

groups = scenes.inject({}) do |new_hash, scene|
   new_hash[scene.number] = [] if new_hash[scene.number].nil?
   new_hash[scene.number] << scene
   new_hash
end

Ruby takes the empty hash passed into inject() and sets new_hash to that. When the block ends the return value gets used to initialize new_hash the next time through, i.e., new_hash keeps accumulating the result of the block.

In your original code you were not returning the hash but an array (new_hash[scene.number] is an array) and the next loop through Ruby complained because new_hash[scene.number] was trying to do a lookup into the array with a string value, hence the error you got.

like image 162
the Tin Man Avatar answered Nov 01 '22 23:11

the Tin Man


Z.E.D.'s right. See Jay Fields' Thoughts: Ruby: inject for a good explanation of inject by example.

As presented, your block returns an array. So the new_hash in |new_hash, scene| ends up being that array. When Ruby tries to find the array index 'one', it throws the error because 'one' is a String, not an Integer.

All you need to do is return new_hash as Z.E.D. showed, and you'll get something like this:

{
  "two" => [
    #<Scene:0x101836470 @number="two">
  ],
  "one" => [
    #<Scene:0x101836510 @number="one">,
    #<Scene:0x1018364c0 @number="one">,
    #<Scene:0x101836420 @number="one">
  ]
}
like image 31
benm Avatar answered Nov 01 '22 22:11

benm