scope problem in controller

I am writing a small helper to walk a hash or an array of arrays to find a nested key. (by the way, if there is an existing function for this in Ruby I will willingly take it instead).

The call is this

keywalk(collection, key)

the def is this

def keywalk(collection,key)   key = key.to_s   collection.each do |check|     return check[1] if check[0] == key     keywalk(check,key) if (check.is_a?(Array) or check.is_a?(Hash)) end nil end

The problem that I have is that key is always nil inside collection.each even though it is define inside the method and a puts shows that it has the value passed as that parameter. Why is key evidently out of scope inside the iterator?

blocks can see variables created outside them (but local variables created inside the block do not persist outside it) Your keywalk function will however almost always return nil - when you recurse and call keywalk a second time you are ignoring the return value

Fred

OK. Then I am missing something simple. And, I am probably trying too hard. I will revisit the problem.

I am clearly misunderstanding something fundamental about ruby methods and the return statement.

I have finally gone for the blunt force solution so that I can follow along with what is happening.

  def keywalk(tcoll,tkey,result=nil)

    if result       puts("result returned = #{result}")       return result     end

    print tcoll.to_yaml     puts("")

    tcoll.each_pair do | tk, tv |       puts("tkey is #{tkey} and tk is: #{tk} and tv is #{tv}")       if tk.to_s == tkey.to_s         puts("keys are equal and tv is #{tv}")         result = { tkey => tv.to_s }         puts("The result is = #{result}")         return       end

      if tv.kind_of?(Array)         puts("Array = #{tv.inspect}")         tv.each do |tva|           if tva.kind_of?(Hash)             keywalk(tva,tkey,result)           end         end       elsif tv.kind_of?(Hash)         puts("Hash = #{tv.inspect} class is #{tv.class}")         keywalk(tv,tkey,result)       else         # puts("Something else = #{tv.inspect} with class # {tv.class}")       end     end     puts("The final result was = #{result}")     result   end

Why, even when I finally find the inner key which this does accomplish, do I always get nil returned? What do I misapprehend about returns?

tkey is user_id and tk is: user_id and tv is 339 keys are equal and tv is 339 The result is = user_id339 This is what keywalk returns:

Quoting byrnejb <byrnejb@harte-lyne.ca>:

I am clearly misunderstanding something fundamental about ruby methods and the return statement.

I have finally gone for the blunt force solution so that I can follow along with what is happening.

  def keywalk(tcoll,tkey,result=nil)

    if result       puts("result returned = #{result}")       return result     end

    print tcoll.to_yaml     puts("")

    tcoll.each_pair do | tk, tv |       puts("tkey is #{tkey} and tk is: #{tk} and tv is #{tv}")       if tk.to_s == tkey.to_s         puts("keys are equal and tv is #{tv}")         result = { tkey => tv.to_s }         puts("The result is = #{result}")         return       end

Do you mean 'return result' here?

Jeffrey

Exactly right.

"return" alone will always return "nil".

Aleksey

Jeffrey L. Taylor wrote:

Quoting byrnejb <byrnejb@harte-lyne.ca>:

      return result         puts("The result is = #{result}")         return       end

Do you mean 'return result' here?

You may also be confused by the ActiveSupport "returning" method.

Jeffrey

Best,

Jeffrey L. Taylor wrote:

> end

Do you mean 'return result' here?

Jeffrey

Thank you for pointing out the error with the return. I fixed that and and then finally realised that I was not assigning my return value when calling the method recursively. This was why the result was disappearing after I found it.

The final code looks like this:

  def keywalk(tcoll,tkey,result=nil)

    tcoll.each_pair do | tk, tv |

      if tk.to_s == tkey.to_s         result = { tkey => tv.to_s }         return result if result       end

      if tv.kind_of?(Array)         puts("Array = #{tv.inspect}")         tv.each do |tva|           if tva.kind_of?(Hash)             result = keywalk(tva,tkey,result)             return result if result           end         end       elsif tv.kind_of?(Hash)         puts("Hash = #{tv.inspect} class is #{tv.class}")         result = keywalk(tv,tkey,result)         return result if result       end     end

    return result

  end

A bit bit wordy but at least I can follow along with it in my head.

Regards,