I for some reason can't get this iteration thing right in my head - ruby

Once again I am a new student to this whole computer coding thing and I am doing a boot camp to try to get the basics and my foot in the door but for some reason I can't make this whole iteration thing stick in my brain we just started doing hashes in ruby and I have literally been staring at the checkpoint problem for a day and a half and I just can't make my brain know what the next logical step is to get the answer provided. It is in a pre work section before my actual live classes start here in a few weeks and it's only my second full week doing any coding at all so the most bare bones basic hints/answers would be greatly appreciated.
This is the problem:
Write a loop to give each person an email address that consists of their first name + last name # gmail.com. For example, Robert Garcia will have an email of robertgarcia#gmail.com. The program should end with: p people
people = [
{
"first_name" => "Robert",
"last_name" => "Garcia",
"hobbies" => ["basketball", "chess", "phone tag"]
},
{
"first_name" => "Molly",
"last_name" => "Barker",
"hobbies" => ["programming", "reading", "jogging"]
},
{
"first_name" => "Kelly",
"last_name" => "Miller",
"hobbies" => ["cricket", "baking", "stamp collecting"]
}
]
outer_index = 0
names = []
last_names = []
while outer_index < people.length
names << people[outer_index]["first_name"].downcase
last_names << people[outer_index]["last_name"].downcase
outer_index += 1
end
email = email = [names[0] + last_names[0] + "#gmail.com"]
this is all the farther I have gotten because everything I've tried to get it to go back trough and pick up the second and third names hasn't worked.
According to them this is what it is supposed to look like in the end:
so that you can see if the correct modifications were made to each hash. The result should be:
people =[
{
"first_name" => "Robert",
"last_name" => "Garcia",
"hobbies" => ["basketball", "chess", "phone tag"],
"email" => "robertgarcia#gmail.com"
},
{
"first_name" => "Molly",
"last_name" => "Barker",
"hobbies" => ["programming", "reading", "jogging"],
"email" => "mollybarker#gmail.com"
},
{
"first_name" => "Kelly",
"last_name" => "Miller",
"hobbies" => ["cricket", "baking", "stamp collecting"],
"email" => "kellymiller#gmail.com"
}
]
(Note that your output won't be indented nicely).
I am completely at a loss and I cannot see where I am going wrong so any help would be insanely helpful so I can get through this checkpoint and finish up week two and move on to week three asap.

It's pretty straightforward to loop over each element of the people array. We also can use string interpolation to easily compose the email address.
people.each do |h|
h["email"] = "#{h["first_name"]}#{h["last_name"]}#gmail.com".downcase
end
If we want to break this up a bit, we can.
people.each do |h|
fn = h["first_name"]
ln = h["last_name"]
h["email"] = "#{fn}#{ln}#gmail.com"
h["email"].downcase!
end

You're really overcomplicating it, there is no need to use while to simply loop across an array. Instead use #each from the Enumerable module:
people.each do |hash|
hash.merge!(
"email" => "#{hash['first_name']}#{hash['last_name']}#gmail.com".downcase
)
end
Or if you want a non-destructive version that doesn't alter the original data:
people.map do |hash|
hash.merge(
"email" => "#{hash['first_name']}#{hash['last_name']}#gmail.com".downcase
)
end

Related

How to merge values of a single hash?

Is there any way to merge values of a single hash?
Example:
address = {
"apartment" => "1",
"building" => "Lido House",
"house_number" => "20",
"street_name" => "Mount Park Road",
"city" => "Greenfield",
"county" => nil,
"post_code" => "WD1 8DC"
}
Could we get an outcome which looks like this?
1 Lido House,
20 Mount Park Road,
Greenfield,
WD1 8DC
address.compact will remove the value which equals nil, but what if in a method you include string interpolation and you want to exclude the nil value for some addresses and include it for others without a comma at the end?
def address(hash)
hash.compact
puts "#{hash["apartment"]} #{hash["building"]}, \n#{hash["house_number"]} #{hash["street_name"]}, \n#{hash["city"]}, \n#{hash["county"]}, \n#{hash["post_code"]}"
end
You need to join the values in a string:
"#{address['house_number']} #{address['street_name']},\n#{address['city']},\n#{address['post_code']}"
You could also improve the formatting by making this a helper method, and using a HEREDOC:
def formatted_address(address)
<<~ADDRESS
#{address['house_number']} #{address['street_name']},
#{address['city']},
#{address['post_code']}
ADDRESS
end
Usage:
address = {
"house_number" => 20,
"street_name" => "Mount Park Road",
"city" => "Greenfield",
"post_code" => "WD1 8DC"
}
puts formatted_address(address)
# => 20 Mount Park Road,
# Greenfield,
# WD1 8DC
Use string formats.
"%{house_number} %{street_name},\n%{city},\n%{post_code}" % address

Iterating through nested hashes named using strings

I'm having some trouble iterating through a series of nested hashes, and I think its because the inner hashes are named with strings. Unfortunately, I cannot simply change these names. Here is a generic hash of the kind that I am working with:
hash =
"name" => {
"stuff" => "value",
"key" => "value", },
"name" => {
"stuff" => "value",
"key" => "value", },
I'm trying to write a program that will print the fields labelled as "name" as well the values, within, when called by the names of their keys. Right now, I am stuck with
hash.each do |key, value|
puts key
key.each do |stuff, info|
puts info if category == "stuff"
end
end
but this gives the error that each is not a recognized method for the key, which is, I think, because the computer is treating it as a string due to its naming. Does anyone have any ideas how I can proceed from here (without changing the names of the keys)?
As #meager says - the sub-hash is in the value. Try this
hash.each do |key, sub_hash|
puts key
sub_hash.each do |category, info|
puts info if category == "stuff"
end
end
What you have is an array of hashes, and not a Hash:
arr = [
{"name" => {"stuff" => "value","key" => "value" }},
{"name" => {"stuff" => "value","key" => "value" }},
{"noname" => {}}
]
arr.each do |hash|
name = hash["name"]
puts name["stuff"] if name
end

Change deep_merge to Utils.deep_merge_hashes

I am using Octopress to generate static html pages. I tried to change the language of the dates using this instruction (it is in German but we need only the code). When I copy date.rb from this German website to my octopress/plugins, I have the following error: Liquid Exception: undefined method `deep_merge' for # in blog/path/to/post/index.html.
I can generate site if I comment out this part in date.rb:
def to_liquid
date_format = self.site.config['date_format']
self.data.deep_merge({
"title" => self.data['title'] || self.slug.split('-').select {|w| w.capitalize! || w }.join(' '),
"url" => self.url,
"date" => self.date,
# Monkey patch
"date_formatted" => format_date(self.date, date_format),
"updated_formatted" => self.data.has_key?('updated') ? format_date(self.data['updated'], date_format) : nil,
"id" => self.id,
"categories" => self.categories,
"next" => self.next,
"previous" => self.previous,
"tags" => self.tags,
"content" => self.content })
end
Then the language is changed for the dates in blog/archives, but not for the dates in posts. I found a similar problem which has been solved by changing deep_merge → Utils.deep_merge_hashes. So I understand that I need to do exactly the same in the piece of the code I presented above. I think it should be quite easy, but since I don't know Ruby, I didn't succeed yet. Could you please tell me how should I use Utils.deep_merge_hashes instead of deep_merge in this case?
This works (ruby 2.1.1 - Jekyll 2.5.3)
def to_liquid(attrs = nil)
date_format = self.site.config['date_format']
new_datas = {
"title" => self.data['title'] || self.slug.split('-').select {|w| w.capitalize! || w }.join(' '),
"url" => self.url,
"date" => self.date,
# Monkey patch
"date_formatted" => format_date(self.date, date_format),
"updated_formatted" => self.data.has_key?('updated') ? format_date(self.data['updated'], date_format) : nil,
"id" => self.id,
"categories" => self.categories,
"next" => self.next,
"previous" => self.previous,
"tags" => self.tags,
"content" => self.content }
Utils.deep_merge_hashes(self.data, new_datas)
end

rails + activerecord: how create a hash from table with particular field's value as key

Given a table ZipCodeInfos with fields zipcode, state, city (all strings), where zipcode is unique:
zipcode,city,state
"10000", "Fooville", "AA"
"10001", "Smallville", "AA"
"10002", "Whoville", "BB"
What is the fastest way to generate a hash object of the entire table where the zipcode is a key like this:
{ "10000" => {:city => "Fooville", :state => "AA" },
"10001" => {:city => "Smallville", :state => "AA" },
"10002" => {:city => "Whoville", :state => "BB" } }
I know for a given record I can use .attributes to generate a hash with key,value pairs of field-names, field-values, for example Zipcode.first.attributes gives me
{"id" => 1, "zipcode" => "10000", "city" => "Fooville", "state => "AA" }
But, short of brute force iterating over each record (via .map), I cannot quite figure out how to create the desired hash with the zipcode as the key for each node of the hash.
This is the best I could come up with, and I suspect there is some nifty Ruby goodness that is faster?
zip_info_hash = {}
ZipCodeInfo.all.map{|x| zip_info_hash[x.zip] =
{'state' => x.state, 'city' => x.city }}
You could also try:
ZipCodeInfos.all.group_by &:zipcode
will get you a hash of zip code to array of ZipCodeInfos activerecords.
You can use inject method.
Here is what I generally use.
def visitors_name_email
visitors.inject({}) do |result, visitor|
result.merge(visitor.name => visitor.email)
end
end
I can't think of a way to avoid map here. I'd make only some minor changes to your code:
zip_info=Hash[*ZipCodeInfo.all
.map{|x| [x.zip, {:city => x.city, :state => x.state}]}
.flatten]

How do I extract the hash from an array of one hash?

I'm writing an API parser at the moment, and I'm working on formatting the data nicely.
So far, I have the following code:
data.each {|season| episodes[season["no"].to_i] = season["episode"].group_by{|i| i["seasonnum"].to_i}}
However, the only issue with this is that the output comes out like this:
8 => {
1 => [
[0] {
"epnum" => "150",
"seasonnum" => "01",
"prodnum" => "3X7802",
"airdate" => "2012-10-03",
"link" => "http://www.tvrage.com/Supernatural/episodes/1065195189",
"title" => "We Need to Talk About Kevin"
}
],
2 => [
[0] {
"epnum" => "151",
"seasonnum" => "02",
"prodnum" => "3X7803",
"airdate" => "2012-10-10",
"link" => "http://www.tvrage.com/Supernatural/episodes/1065217045",
"title" => "What's Up, Tiger Mommy?"
}
]
}
So there's a redundant array in each value of the secondary hash. How would I remove this array and just have the inside hash? So, for example I want:
8 => {
1 => {
"epnum" => "150",
"seasonnum" => "01",
"prodnum" => "3X7802",
"airdate" => "2012-10-03",
"link" => "http://www.tvrage.com/Supernatural/episodes/1065195189",
"title" => "We Need to Talk About Kevin"
}
,
etc.
EDIT: Here's the full file:
require 'httparty'
require 'awesome_print'
require 'debugger'
require 'active_support'
episodes = Hash.new{ [] }
response = HTTParty.get('http://services.tvrage.com/feeds/episode_list.php?sid=5410')
data = response.parsed_response['Show']['Episodelist']["Season"]
data.each { |season|
episodes[season["no"].to_i] = season["episode"].group_by{ |i|
i["seasonnum"].to_i
}
}
ap episodes
Input data: http://services.tvrage.com/feeds/episode_list.php?sid=5410
Wild guess:
data.each { |season|
episodes[season["no"].to_i] = season["episode"].group_by{ |i|
i["seasonnum"].to_i
}.first
}
It looks like you're using group_by (array of entries with same key) when you really want index_by (one entry per key).
data.each {|season| episodes[season["no"].to_i] = season["episode"].index_by {|i| i["seasonnum"].to_i}}
NOTE: If you can have MORE than one episode with the same seasonnum, you SHOULD use group by and have an array of values here. If you're just building a hash of episodes with a convenient lookup (one to one mapping), then index_by is what you want.

Resources