2013-03-31 1 views
0

Я пытаюсь вернуть список значений на основе пользовательских аргументов из хэшей, определенных в локальной среде.undefined method `assoc 'for # <Hash: 0x10f591518> (NoMethodError)

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"] 
+0

Какая версия Ruby? 'Hash # assoc' был добавлен в Ruby 1.9. Он не доступен в Ruby 1.8. –

+0

1.9.3 извините, я пропустил это – aug2uag

+0

Возможно, он находится в другом драгоценном камне, который загружается консолью irb, но не при запуске скрипта. – User

ответ

1

Я предполагаю, что вы пришли к Руби из какого-то другого языка, так как есть много ненужного хлама в этом методе. Кроме того, он не вернет то, что вы ожидаете по целому ряду причин.

`accumulator = Hash.new(0)` 

Это не нужно, так как (1), вы ожидаете, массив должен быть возвращен, и (2), вам не нужно предварительно инициализировать переменные в рубин.

В этом контексте синтаксис Hash[...] является нетрадиционным и обычно используется для преобразования некоторого другого перечислимого (обычно массива) в хэш, как в Hash[1,2,3,4] #=> { 1 => 2, 3 => 4}. Когда вы определяете хеш, вы можете просто использовать фигурные скобки { ... }.

Для каждой итерации args вы назначаете аккумулятор на результат поиска хешей вместо накопления значений (что на основе вашего вывода на примере - это то, что вам нужно сделать). Вместо этого, вы должны смотреть на различные методы массива конкатенации как push, +=, << и т.д.

Как это выглядит, как вы не нужны ключи в результате assoc, вероятно, слишком много. Вам будет лучше обслуживать fetch или простой поиск в скобках (hash[key]).

Наконец, хотя вы можете вызывать любой метод в Ruby с блоком, как вы это делали, с to_a, если только метод не дает значения блоку, Ruby проигнорирует его, поэтому [k].product(v).flatten на самом деле ничего не делает ,

Я не хочу быть слишком критичным. Синтаксис Ruby чрезвычайно гибкий, но также относительно компактный по сравнению с другими языками, что означает, что его легко занять слишком далеко и в итоге трудно понять и трудно поддерживать методы.

Существует еще один побочный эффект того, как создается ваш метод, в котором аккумулятор будет собирать только значения из первого хэша, который имеет конкретный ключ, даже если более одного хэша имеет этот ключ. Поскольку я не знаю, намеренно это или нет, я сохраню эту функциональность.

Вот версия вашего метода, который возвращает то, что вы ожидаете:

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