У меня есть код:
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 может знать тип этой переменной?
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">
]
}
Почему бы не использовать 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
с блоком, вероятно, будет уместно, если вы хотите применить какой-то алгоритм во время слияния или группировки.
Кроме того, чтобы объяснить, как Ruby может узнать тип этого переменная 'и почему она пытается' преобразовать строку в целое число ', вы, возможно, захотите пересмотреть: Переменные Ruby и динамическая типизация .
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] пытался выполнить поиск в массиве со строковым значением, отсюда и ошибка.
Я знаю, что ответ на этот вопрос принят, но не могу не опубликовать свой ответ.
groups = scenes.inject({}) { |nh, s| nh.tap {|h| (h[s.number] ||= []) << s } }