类型错误:无法将字符串转换为整数。

5

我有一段代码:

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从字符串转换为整数?

关于“inject”方法还有一个额外的问题。 Ruby何时初始化“new_hash”变量,以及Ruby如何知道此变量的类型?


1
顺便说一下:你可以更轻松地使用groups = scenes.group_by(&:number)来实现你想要的效果,而不是使用inject。 - sepp2k
新的哈希变量被初始化为注入的值(请记住:这是一个折叠操作,在累加器中有一个开始值)。 - hurikhan77
5个回答

10

尝试:

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] 正在尝试使用字符串值进行查找,因此出现了错误。


@shingara:不,它没有失败。他的代码运行无误并返回预期的结果。 - sepp2k
1
谢谢sepp2k。我同意,“为什么被downvote了?”它运行正常,修复了他的代码中的错误并解释了问题。 - the Tin Man

6
Z.E.D.是正确的。参见Jay Fields' Thoughts: Ruby: inject,了解inject的一个很好的例子。
按照目前的方式,您的块返回一个数组。因此,在|new_hash, scene|中的new_hash最终就是那个数组。当Ruby尝试查找数组索引“one”时,它会抛出错误,因为“one”是字符串,而不是整数。
您只需要像Z.E.D.所示返回new_hash即可,您将得到以下结果:
{
  "two" => [
    #<Scene:0x101836470 @number="two">
  ],
  "one" => [
    #<Scene:0x101836510 @number="one">,
    #<Scene:0x1018364c0 @number="one">,
    #<Scene:0x101836420 @number="one">
  ]
}

2
为什么不使用 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

我知道这个问题已经有一个被接受的答案了,但我还是忍不住要发表我的答案。

groups = scenes.inject({}) { |nh, s| nh.tap {|h| (h[s.number] ||= []) << s } }

0
此外,为了解释“Ruby如何知道此变量的类型”以及为什么它试图“将字符串转换为整数”,您可能需要查看:Ruby变量和动态类型

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接