Ошибка типа: невозможно преобразовать строку в целое число - PullRequest
5 голосов
/ 21 марта 2010

У меня есть код:

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

Когда я запускаю его, я получаю сообщение об ошибке:

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

Если я изменю сцены на:

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

проблема исчезнет.

Почему я получаю сообщение об ошибке в первом случае? Почему Ruby решил преобразовать scene.number из String в Integer?

И еще один вопрос о методе «инъекции». Когда Ruby инициализирует переменную 'new_hash' и как Ruby может узнать тип этой переменной?

Ответы [ 5 ]

10 голосов
/ 21 марта 2010

попробовать:

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 берет пустой хеш, переданный в inject (), и устанавливает для него new_hash. Когда блок заканчивается, возвращаемое значение используется для инициализации new_hash в следующий раз, то есть new_hash продолжает накапливать результат блока.

В исходном коде вы возвращали не хеш, а массив (new_hash [scene.number] - массив), и следующий цикл в Ruby жаловался, потому что new_hash [scene.number] пытался выполнить поиск в массив со строковым значением, следовательно, вы получили ошибку.

6 голосов
/ 21 марта 2010

З. Е.Д. прав. См. Мысли Джея Филдса: Ruby: inject для хорошего объяснения inject на примере.

Как показано, ваш блок возвращает массив. Таким образом, new_hash в |new_hash, scene| оказывается тем самым массивом. Когда Ruby пытается найти индекс массива 'one', он выдает ошибку, потому что 'one' является строкой, а не целым числом.

Все, что вам нужно сделать, это вернуть new_hash как Z.E.D. показал, и вы получите что-то вроде этого:

{
  "two" => [
    #<Scene:0x101836470 @number="two">
  ],
  "one" => [
    #<Scene:0x101836510 @number="one">,
    #<Scene:0x1018364c0 @number="one">,
    #<Scene:0x101836420 @number="one">
  ]
}
2 голосов
/ 21 марта 2010

Почему бы не использовать group_by , что, вероятно, именно то, что вы пытаетесь включить?

groups = scenes.group_by(&:number)
# => {"two"=>[#<Scene:0xb728ade0 @number="two">],
#     "one"=>
#       [#<Scene:0xb728ae30 @number="one">,
#        #<Scene:0xb728ae08 @number="one">,
#        #<Scene:0xb728ada4 @number="one">]}

inject - это операция складывания, а не совсем то, что вы хотите. По крайней мере, это неудобно для использования таким образом. merge с блоком, вероятно, будет уместно, если вы хотите применить какой-либо алгоритм во время слияния или группировки.

0 голосов
/ 25 марта 2010

Я знаю, что ответ на этот вопрос принят, но я не могу не опубликовать свой ответ.

groups = scenes.inject({}) { |nh, s| nh.tap {|h| (h[s.number] ||= []) << s } }
0 голосов
/ 21 марта 2010

Кроме того, чтобы объяснить, «как Ruby может узнать тип этой переменной» и почему он пытается «преобразовать строку в целочисленное значение», вы, возможно, захотите изменить: Переменные Ruby и динамическая типизация .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...