未定义的方法`assoc'用于#<hash:0x10f591518>(NoMethodError)</hash:0x10f591518>

时间:2013-03-31 08:02:00

标签: ruby hash enumerable

我正在尝试根据用户定义的参数返回值列表,这些参数来自本地环境中定义的哈希值。

def my_method *args
  #initialize accumulator
  accumulator = Hash.new(0)

  #define hashes in local environment
  foo=Hash["key1"=>["var1","var2"],"key2"=>["var3","var4","var5"]]
  bar=Hash["key3"=>["var6"],"key4"=>["var7","var8","var9"],"key5"=>["var10","var11","var12"]]
  baz=Hash["key6"=>["var13","var14","var15","var16"]]

  #iterate over args and build accumulator
  args.each do |x|
    if foo.has_key?(x)
        accumulator=foo.assoc(x)
    elsif bar.has_key?(x)
        accumulator=bar.assoc(x)
    elsif baz.has_key?(x)
        accumulator=baz.assoc(x)
    else
        puts "invalid input"
    end
end

  #convert accumulator to list, and return value
  return accumulator = accumulator.to_a {|k,v| [k].product(v).flatten}
end

用户使用作为关键字的参数调用方法,并返回与收到的每个关键字相关联的值列表的函数。

例如

> my_method(key5,key6,key1)
=> ["var10","var11","var12","var13","var14","var15","var16","var1","var2"]

输出可以是任何顺序。我尝试运行代码时收到以下错误:

undefined method `assoc' for #<Hash:0x10f591518> (NoMethodError)

请你指点我如何解决这个问题?在终端assoc中完全按照我的预期执行:

> foo.assoc("key1")
=> ["var1","var2"]

1 个答案:

答案 0 :(得分:1)

我猜你是用其他语言来Ruby的,因为这种方法有很多不必要的瑕疵。此外,由于各种原因,它不会返回您的期望。

`accumulator = Hash.new(0)`

这是不必要的,因为(1),你期望返回一个数组,(2),你不需要在ruby中预先初始化变量。

Hash[...]语法在此上下文中是非常规的,通常用于将其他一些可枚举(通常是数组)转换为哈希,如Hash[1,2,3,4] #=> { 1 => 2, 3 => 4}中所示。在定义哈希时,可以使用大括号{ ... }

对于args的每次迭代,您将累加器分配给散列查找的结果而不是累积值(根据您的示例输出,您需要执行此操作)。相反,您应该查看各种数组连接方法,如push+=<<等。

由于看起来你不需要结果中的键,assoc可能有点过分。使用fetch或简单括号查找(hash[key])可以提供更好的服务。

最后,虽然您可以使用块调用Ruby中的任何方法,但正如您使用to_a所做的那样,除非该方法专门为块生成值,否则Ruby将忽略它,因此{{1}实际上并没有做任何事情。

我并不是说过于批评 - Ruby的语法非常灵活,但与其他语言相比也相对紧凑,这意味着它很容易把它拿得太远而最终难以理解和难以维护的方法。 / p>

构造方法的另一个副作用是,累加器只会从具有特定键的第一个哈希值中收集值,即使多个哈希值具有该键。由于我不知道这是否有意,我将保留此功能。

以下是返回预期内容的方法版本:

[k].product(v).flatten

一般情况下,我不会定义一个内置数据的方法,但我会将其保留在更接近原始方法的位置。 def my_method(*args) foo = { "key1"=>["var1","var2"],"key2"=>["var3","var4","var5"] } bar = { "key3"=>["var6"],"key4"=>["var7","var8","var9"],"key5"=>["var10","var11","var12"] } baz = { "key6"=>["var13","var14","var15","var16"] } merged = [foo, bar, baz].reverse.inject({}, :merge) args.inject([]) do |array, key| array += Array(merged[key]) end end 组合两个哈希并覆盖原始哈希中的任何重复键与参数哈希中的重复键。即使密钥不存在,Hash#merge调用也会强制执行数组,因此您无需显式处理该错误。

我建议您查看Array()方法 - 它非常通用,在许多情况下都很有用。 inject使用自己的累加器变量(可选地定义为参数),该变量作为第一个块参数输出到块。