Welcome to the Treehouse Community

Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.

Start your free trial

Ruby

Ruby Hashes Extra Credit. Too hard?

"Write a function that converts hashes with string keys into hashes with symbols as keys. The function should work recursively."

I've been having a go at this, but I'm on the verge of giving up.

I've been googling to help myself, and there does seem to be some solutions on various forums, but I'd just be copy/pasting without really understanding what's going on.

At this stage of the tutorial it seems a bit too hard. Unless I'm missing something? I've been experimenting with myhash.map and myhash.each_key, but basically I'm pretty stumped.

5 Answers

I did it Maciej!!!

I am SO pleased, I had a little celebration. Us English people would say, "YOU BEAUTY!!!". But even though I'm not Scottish, I prefer the Scot's "YA DANCAAAAA", which means "you dancer", which translates to, "I'm really really pleased."

Thanks for all your help - you provided just enough help, without helping 'too much'.

myhash = {"Name"=>"Andy", "Sex"=>"Male", "Status"=>"Awesome"}

def convert(myhash)

    @newhash = {}

    myhash.map do |key, value| 
        newkey = key.to_sym
        newvalue = value
        @newhash[newkey]=newvalue
    end

end

convert(myhash)

puts @newhash

Having said all that, PLEASE don't be afraid to criticise my code. Can you see any problems with it?

I used @ to make newhash accessible outside of the method. It's the only way I know to achieve that. Is there another way to make the newhash accessible outside of the method?

Congrats, Andrew :). I can't see anything wrong with your code, but then again, I'm not a real developer yet :).

Mind telling me why you used an instance variable? I thought you only use that when you initialize something in a class. Why did you use it in a basic method rather then a regular variable to set the array too?

Alphonse Cuccurullo it's to do with Scope, as I alluded to in the answer. However, off the top of my head, I'm not exactly sure why. I'm sure I could find out with some googling, but I suggest you do that yourself and tell me if you're still stumped and why and I'll help you out.

Still stumped

Alphonse Cuccurullo I can't help you unless you tell me what you've done to try to 'unstump' yourself and what exactly it is that you don't understand.

I just wanna know the purpose behing you using the @newhash rather then just hash = []. That's all really.

I just wanna know the purpose behing you using the @newhash rather then just hash = []. That's all really.

Alphonse Cuccurullo, again, it's to do with Scope, as I alluded to in the answer. However, off the top of my head, I'm not exactly sure why. I'm sure I could find out with some googling, but I suggest you do that yourself and tell me if you're still stumped and why and I'll help you out.

I dont know what you mean by scope bro. More specific detail's would be fantastic.

Alphonse Cuccurullo. Dude, you really need to get into the habit of googling so you can get good at it. If/when you become professional, you still have to do that all the time. Being good at googling to learn stuff is just as important a skill as knowing how to use git, for example. No joke.

This might sound harsh, but it's for your own good. You don't know what Scope means, so google "ruby scope".

Alphonse Cuccurullo I've just realised that this solution isn't recursive anyway.

I've gone to the trouble of creating some homework for you that I think will be a very useful exercise for you, for a number of reasons.

I've created a repo with a non-recursive solution. The solution is verified using a test I created with rspec.

Follow the instructions in the Readme to clone the repo to your laptop and get to the point where you can run rspec to execute the test. That's the first task.

Second is to rewrite the convert method so it's recursive. Google recursive to find out what that means.

Here's the repo: https://github.com/Yorkshireman/string_keys_to_syms

Whenever you make a change to the convert method, you can check if it works properly by just running rspec. You simply rewrite the method so it's recursive - you'll know it's correct if the test still passes.

Lol thanks for the help man and tolerating me as well greatly appreciate it. Had no idea there were that many different types of variable's. I am fairly new at coding i think i just gotta take one thing at a time to get it crammed into my mind. Coding is stressful especially when you gotta keep a eye on all the pieces of syntax that synergize with eachother.

I can give you some hints. You can then read up on the methods, experiment with these and see if you can get it to work.

Start with defining the method and making it accept an argument (which will be a hash in this case). Something like this:

def convert(hash)
   //your code
end

There is a thing called .map and it looks like this:

hash.map { |key,value|
        // your code here
    }

This allows you to do things to each key and value of the hash that you have in the hash variable.

There is also a method that lets you convert things into symbols: .to_sym, which will be useful here.

At least this is the way I would do it. There are probably other approaches to this. Those extra credit things are always harder and they use stuff that isn't in the videos.

It looks like I'll have to come back to this after I've gone through the Method module - this is what I mean - this task seems out of sync with the tutorials. I know I'm complaining here, but I'm not one for complaining for the sake of it (I do believe in the 'stop whining and get on with it' approach) - I'm just feeding back that the extra credit things could be better constructed.

I'll get on with getting on with it now ;-) Thanks for the advice, it will help a lot and I'll post up my code here once I've solved the problem.

I don't understand why I should use .map instead of .each_key? In my code below, using either gives the same result.

Like I said, .map is something I know and something I would use, there are probably more ways to do it :). Although, I'm not sure how to use each_key to return the whole hash. I only tried this with map and it worked for me.

I'm still having trouble with this. I don't want to be given the answer (I haven't looked at Bill's link yet! :-) ), I'd rather be given hints and have specific questions answered. So:

@myhash = {"Name"=>"Andy", "Sex"=>"Male", "Status"=>"Awesome"}

def convert
    @myhash.each_key do |key| 
        key.to_sym
        print key.class
    end
end

convert

This returns StringStringString Which means that the keys were NOT converted to symbols! Why?

This doesn't make sense in light of:

@myhash = {"Name"=>"Andy", "Sex"=>"Male", "Status"=>"Awesome"}

def convert
    @myhash.each_key do |key| 
        key="newkey"
        print key
    end
end

convert

This returning newkeynewkeynewkey !

Why does my method for converting each key into "newkey" works, but my method for converting the keys into symbols NOT work?!

Ok, I solved this problem, with some experimentation, BUT I REALLY NEED SOMEONE TO EXPLAIN WHY.

So, it was solved simply by using key = key.to_sym instead of just key.to_sym

@myhash = {"Name"=>"Andy", "Sex"=>"Male", "Status"=>"Awesome"}

def convert
    @myhash.each_key do |key| 
        key = key.to_sym
        print key.class
    end
end

convert

This returns SymbolSymbolSymbol

So, in Ruby, there are two kinds of methods. Some methods just do stuff, return the result, but don't modify the object they are used on (they just return the new value in the line they are called, and in the next line, the object is back to its original form) and in many cases, these same methods have a parallel method which ends with the ! symbol ("unsafe methods") and such methods permanently change the object it was used on. The method you used here:

key.to_sym

is a "safe" method, which means it does not modify the key object - it just returns the key as symbol in this line, and after this line the key is back to its original form (String). But if you assign this returned value to the key variable (key = key.to_sym), it will be changed permanently, so in the next line, you no longer have a string - you have a symbol and so Symbol class is returned. Hope this helps. (not sure if there's an unsafe version of to_sym, I just mentioned "unsafe" methods as a general tip I learned at some point ;) ).

Thanks Maciej. It makes sense.

What I'm trying to work out now, is how to PERMANENTLY alter the keys i.e. change them into symbols inside the method (like I have), but when running 'puts "Name".class' for example, afterwards, outside the method, I want it to return 'Symbol'.

Any hints?

I would suggest simply building a completely new hash, at each iteration putting a modified key and value from the original hash, and then returning that new hash instead of making a permanent change to the original one.

Yep, I had a similar idea. Thanks for the confirmation. My head hurts, but I'll be the happiest man alive when I finally solve this thing.

Yeah, I also was a bit confused with this because ' .to_sym ' was never introduced in the videos. Other, then that everything else is self explanatory thanks to Maciej advice.

I found a solution here (http://stackoverflow.com/questions/8379596/how-do-i-convert-a-ruby-hash-so-that-all-of-its-keys-are-symbols)

I also spent ages on this reading all the forum posts. I eventually came across this, which is an alternative to the OP solution. I did copy it but the journey to understanding how it works is important part for me.

matthash = {"name" => "Matt", "superhero" => "batman"}

def convert(matthash)

    @newhash = {}
    @newhash = matthash.map {|key, value| [key.to_sym, value]}.to_h
end

convert(matthash)

puts "Original hash #{matthash}"
puts "Converted hash #{@newhash}"