Becoming More Selective - Ruby - Fine Tuning


#1

Working on the code below, the output comes through properly and displays movies with a rating > 3.

My goal is now to properly format the output, by capitalizing each word in the title and removing each underscore to replace it with a space. I'm new to programming and iterating over the hash is proving challenging. Suggestions very welcome!

movie_ratings = {
memento: 3,
primer: 3.5,
the_matrix: 5,
truman_show: 4,
red_dawn: 1.5,
skyfall: 4,
alex_cross: 2,
uhf: 1,
lion_king: 3.5
}

good_movies = movie_ratings.select {|name, rating| rating > 3 }
good_movies.each do |name, rating|
puts ("#{name} has a rating of #{rating}.")
end


#2

You'll need a few methods:

to_s
gsub
split
collect
join

This is the order in which I used them to accomplish what you want.

Here is the Ruby documentation, you can search for the methods and see how they work:

http://ruby-doc.org/core-2.2.3/

As an additional hint, you'll make your job easier if you chain methods together (not all of them, I don't think you'll be able to make all the work on one single line, but if you manage, I'd be glad to have a look at your code).

Play around with that and if you're stuck, publish your new code.

Good luck.

EDIT: nevermind, you can do all the work in one single line if you chain your methods properly.


#3

Are you suggesting that one additional line of code can complete this task, or that by editing the existing code with additional methods it can be completed?

Still pretty new so I'm getting the hang of it, sorry if that seems like a silly question. Thanks for the help though and I'll definitely post my code when I feel I've got something closer, or if I'm totally stumped.


#4

You just need one more line of code inside the code you provided above. This line will need all 5 methods chained together.

If you're not comfortable with chaining methods, it also works with more lines, it's just less elegant :smile:.


#5

Don't like throwing in the towel so quickly, but I feel that it may be better for the learning process to see an example of how this is done.

This isn't even in the scope of the assignment within the tutorial, just an extra puzzle I added for myself since I'm trying to bring it all together. If you're willing to share your solution, I might be able to gain more insight from that.


#6

Let's break it down.

You first need to replace all "_" with " ". The method gsub will do that for you.

But the method gsub is used on string objects, and your hash has symbols. That's why you first need the to_s method, which will convert your symbols into strings, so that they can be processed by gsub.

Try to implement this change first, we'll see about the rest later on.

Keep in mind that you only need to add to your existing code, you don't need to take anything away. I'll help you a bit more and tell you that your change needs to be inside your each method.


#7

Alright my friend fair enough and thank you for taking the time to help me.

What you've explained is where my logic had taken me, but I was having difficulty converting the symbols into strings. I had to take a break for a while but I'm back at it now and I'll update you when I've got something!


#8

The output doesn't display any errors with the following code, but it also doesn't seem to be doing what I'm going for.

I'm sure that I've got some variables and arrays doing bad things, but I'd appreciate you shining some light on the logic flaws.

movies_str = []
good_movies = movie_ratings.select {|name, rating| rating > 3 }
good_movies.each do |name, rating|
movies_str.each {
|title| good_movies.push(
good_movies.to_s.split.gsub(/_/, " ").collect(String).join(String).puts(title.capitalize)
)
}
puts ("#{name} has a rating of #{rating}.")
end


#9

You're approaching the problem in an overly complicated manner.

You don't need another hash or another array to do what you want, since your goal is simply to format the output.

Inside your each scanning through good_movies, you have all the keys and values available via the name and rating variables. Since you're only concerned about formatting the title, you only need to work on name.

Get rid of your movies_str array and instead work on name directly. For now just try the to_s and gsub. If it works it won't be hard to add the rest.

Your code should look like this:

scan through good_movies using |name, rating|
modify name
print name and rating
end

#10

Most recent iteration, getting the following error though:

undefined method `gsub' for ["primer"]:Array

good_movies = movie_ratings.select {|name, rating| rating > 3 }
good_movies.each do |name, rating|
name.to_s.split.gsub(/_/, " ")
puts ("#{name} has a rating of #{rating}.")
end


#11

You're trying to use the split method on name and give it to gsub, but gsub expects a string object, not an array object (yes, the split method returns an array).

You don't need split for now, since all you want is getting rid of the "_" characters. The split will come after this step.


#12

This passes, but I'm unsure (even after some research and fooling about) where to go from here. Don't think I've encountered collect and join before.

good_movies = movie_ratings.select {|name, rating| rating > 3 }
good_movies.each do |name, rating|
name.to_s.gsub(/_/, " ").split
puts ("#{name} has a rating of #{rating}.")
end


#13

Don't forget that you want to MODIFY name, not simply use methods on it.

Now that we have our string with spaces with the help of to_s and gsub, we want to capitalize our name.

However, we want to capitalize all separate words inside our name, so the simple capitalize method will not be enough.

What would be great would be a way to separate our words inside our name variable so that we can apply capitalize to all of them, and then join back all these words into one string.

The separation will be made using the method split, we will then apply the method capitalize on all elements of the array (since split returns an array) using the collect method, and finally we will merge together what the collect method returns with the method join.

After all this, our name variable should contain our title formated properly, and it will be ready to be printed.

Here is the process schematized with name containing :the_ring :

:the_ring => to_s => "the_ring" => gsub => "the ring" => split => ["the", "ring"] => collect (and capitalize) => ["The", "Ring"] => join => "The Ring"


#14

Let's go back a step. Based on the code I entered last time, why isn't the output producing titles without spaces? Does it all have to be done together to work?

My understanding might be flawed but I can't rightly understand why the puts statement at the end isn't showing the movie titles displayed as "the matrix has a rating of x"


#15

Because you didn't modify name, you simply used methods on it without doing anything with the result, so puts still has access to the name unmodified.

If you want to modify a variable, you should use the = operator.


#16

I get that you're trying to help me come to the right answer, but I've been doing this for about a week and I just have no idea where it should go. That's what I've been trying.

good_movies = movie_ratings.select {|name, rating| rating > 3 }
good_movies.each do |name, rating|
name = name.to_s.gsub(/_/, " ").split

puts ("#{name} has a rating of #{rating}.")
end


#17

That's better, but why do you insist on splitting without doing the rest of the methods?

If you split a string object, for example "the matrix", then you will obtain an array of strings, like ["the", "matrix"]. That's not the end result you want, but it's great for an intermediary step though.

Now, we need to chain the collect method to your line. The collect method can be called on an array (that's what split gives us), and will apply another method to each element of this array. What do we want to do with all these words? We want to capitalize them, so we'll give the method capitalize to collect.

The syntax is the following:

collect(&:the_method_we_need)

Add this and we're almost done, we'll only need to add the join part.


#18

I mean I've been programming for like a week. This is all very new to me.


#19

You're doing just fine so far.

You wanted to do something which was not asked in the exercise. Often, it involves exploring something outside of your knowledge to do so, especially if you're new to the language.

If you want to stop here it's fine by me, but it would mean that your original initiative will lose its sense don't you agree?

Take a deep breath, take your time to read once more everything we discussed. Then, if there are some things which are not clear up to this point, ask me and I'll try to explain the best I can. If there are expressions or words I used that seem obscure, same thing.


#20

I think we have great success. This doesn't pass for the exercise, but the output seems to be displaying the way I wanted, and I'll be able to reference back to this for help with some more complex exercises too! Let me know if there is anything you would alter, but here is what I got:

good_movies = movie_ratings.select {|name, rating| rating > 3 }
good_movies.each do |name, rating|
name = name.to_s.gsub(/_/, " ").split.collect(&:capitalize).join(" ")

puts ("#{name} has a rating of #{rating}.")
end

Thanks for being so patient with me and offering clear explanations!