TypeError: не может преобразовать Строку в Целое число

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

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

Когда я - lauching это, я получаю ошибку:

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)]

проблема dissapear.

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

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

5
задан demas 21 March 2010 в 09:42
поделиться

5 ответов

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

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

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

{
  "two" => [
    #<Scene:0x101836470 @number="two">
  ],
  "one" => [
    #<Scene:0x101836510 @number="one">,
    #<Scene:0x1018364c0 @number="one">,
    #<Scene:0x101836420 @number="one">
  ]
}
6
ответ дан 18 December 2019 в 09:06
поделиться

Почему бы не использовать 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 с блоком, вероятно, будет уместно, если вы хотите применить какой-то алгоритм во время слияния или группировки.

2
ответ дан 18 December 2019 в 09:06
поделиться

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

0
ответ дан 18 December 2019 в 09:06
поделиться

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

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

10
ответ дан 18 December 2019 в 09:06
поделиться

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

groups = scenes.inject({}) { |nh, s| nh.tap {|h| (h[s.number] ||= []) << s } }
0
ответ дан 18 December 2019 в 09:06
поделиться
Другие вопросы по тегам:

Похожие вопросы: