Iterate an array of hashes - ruby

I have a hash with a key of cities and the value is an array of hashes containing location data. It looks like this:
#locations = {
"cities"=>[
{"longitude"=>-77.2497049, "latitude"=>38.6581722, "country"=>"United States", "city"=>"Woodbridge, VA"},
{"longitude"=>-122.697236, "latitude"=>58.8050174, "country"=>"Canada", "city"=>"Fort Nelson, BC"},
...
]
}
I'd like to iterate through and print all the values for the key city:
Woodbridge, VA
Fort Nelson, BC
...

I can't say why would you have that structure, anyway, in the data format you have above, you would access it like
#locations[1].each { |c| p c["city"] }
Although, this implies that you should always expect second object in the array to be the required cities array. Further you need to put in required nil check.

For your corrected data format:
#locations = { "cities"=>[
{ "longitude"=>-77.2497049,
"latitude"=>38.6581722,
"country"=>"United States",
"city"=>"Woodbridge, VA"},
{ "longitude"=>-122.697236,
"latitude"=>58.8050174,
"country"=>"Canada",
"city"=>"Fort Nelson, BC" }] }
#locations["cities"].each { |h| puts h["city"] }
Woodbridge, VA
Fort Nelson, BC
or to save in an array:
#locations["cities"].each_with_object([]) { |h,a| a << h["city"] }
#=> ["Woodbridge, VA", "Fort Nelson, BC"]

As suggested by others, you have to do the exact same thing but let me explain whats happening in there.
Your example is an array and has multiple elements which could be just string like cities or an array of hashes like you mentioned.
So in order to iterate through the hashes and get the city values printed, you first of all have to access the array that has hashes. By doing so
#locations["cities"]
=> [{"longitude"=>-77.2497049, "latitude"=>38.6581722, "country"=>"United States", "city"=>"Woodbridge, VA"}, {"longitude"=>-122.697236, "latitude"=>58.8050174, "country"=>"Canada", "city"=>"Fort Nelson, BC"}]
Now that you have go the array you required, you can just integrate through them and get the result printed like this
#locations["cities"].map{|hash| p hash['city']}
In case your getting nil errors as you have stated in comments, just see what happens when you try to access the array of hashes. if you still are experiencing issues, then you may have to provide the full input so as to understand where the problem is.

Related

How can i sort an arrays of hashes in ruby

this is the code i tried to sort it by name,email,country,comments.first i tried sorting by names
array_of_hashes=[
{"Name"=>"Akash","Email"=>"akash85#gmail.com","Country"=>"India",'Comments'=>"9898984523"},
{"Email"=>"rahul#hotmail.com","Country"=>"Srilanka","Name"=>"Rahul"},
{"Country"=>"India", "Comments"=>"3455358782","Email"=>"veera#gmail.com","Name"=>"Veera"},
{"Name"=>"Akash","Country"=>"India", "Email"=>"akash37#yahoo.com", "Comments"=>"8898788932"}
]
puts array_of_hashes.sort_by { |element| element.keys(&:Name)}
but the displayed output is not as i expected,it prints the same which i mentioned above.
i expected to code the final output should be like this
Name Email Country Comments
Akash akash37#live.com India 8898788932
Akash akash85#gmail.com India 9898984523
Rahul rahul#hotmail.com Srilanka
Veera veera#gmail.com India 3455358782
Help me to resolve these.Thanks in advance!
You should look at what element.keys(&:Name) evaluates to in the block that you're passing to #sort_by. All methods in Ruby can be given a block, you can pass it even if the method doesn't use it. Hash#keys doesn't use the block so element.keys(&:Name) is the same as element.keys and you end up trying to sort by the array ['Name', 'Email', 'Country', 'Comments'].
If you want to sort by the name, say so:
hash.sort_by { |element| element['Name'] }
Keep in mind that your keys are strings so you want element['Name'] rather than element[:Name]. I'd also recommend that you don't call your array of hashes hash, that's a little confusing.
not_a_hash = [
{"Name"=>"Akash","Email"=>"akash85#gmail.com","Country"=>"India",'Comments'=>"9898984523"},
{"Email"=>"rahul#hotmail.com","Country"=>"Srilanka","Name"=>"Rahul"},
{"Country"=>"India", "Comments"=>"3455358782","Email"=>"veera#gmail.com","Name"=>"Veera"},
{"Name"=>"Akash","Country"=>"India", "Email"=>"akash37#yahoo.com", "Comments"=>"8898788932"}
]
sorted_values = not_a_hash.map{|h| h.values_at("Name", "Email", "Country", "Comments")}.sort

How to parse two elements from a list to make a new one

I have this input repeated in 1850 files:
[
{
"id"=>66939,
"login"=>"XXX",
"url"=>"https://website.com/XX/users/XXX"
},
...
{}
]
And I wanted to make a list in a way that by looking for the login I can retrieve the ID using a syntax like:
users_list[XXX]
This is my desired output:
{"XXX"=>"66570", "XXX"=>"66570", "XXX"=>"66570", "XXX"=>"66570", ... }
My code is:
i2 = 1
while i2 != users_list_raw.parsed.count
temp_user = users_list_raw.parsed[i2]
temp_user_login = temp_user['login']
temp_user_id = temp_user['id']
user = {
temp_user_login => temp_user_id
}
users_list << user
i2 += 1
end
My output is:
[{"XXX":66570},{"XXX":66569},{"XXX":66568},{"XXX":66567},{"XXX":66566}, ... {}]
but this is not what I want.
What's wrong with my code?
hash[key] = value to add an entry in a hash. So I guess in your case users_list[temp_user_login] = temp_user_id
But I'm unsure why you'd want to do that. I think you could look up the id of a user by having the login with a statement like:
login = XXX
user = users_list.select {|user| user["login"] == login}.first
id = user["id"]
and maybe put that in a function get_id(login) which takes the login as its parameter?
Also, you might want to look into databases if you're going to manipulate large amounts of data like this. ORMs (Object Relational Mappers) are available in Ruby such as Data Mapper and Active Record (which comes bundled with Rails), they allow you to "model" the data and create Ruby objects from data stored in a database, without writing SQL queries manually.
If your goal is to lookup users_list[XXX] then a Hash would work well. We can construct that quite simply:
users_list = users_list_raw.parsed.each.with_object({}) do |user, list|
list[user['login']] = user['id']
end
Any time you find yourself writing a while loop in Ruby, there might be a more idiomatic solution.
If you want to keep track of a mapping from keys to values, the best data structure is a hash. Be aware that assignment via the array operator will replace existing values in the hash.
login_to_id = {}
Dir.glob("*.txt") { |filename| # Use Dir.glob to find all files that you want to process
data = eval(File.read(filename)) # Your data seems to be Ruby encoded hash/arrays. Eval is unsafe, I hope you know what you are doing.
data.each { |hash|
login_to_id[hash["login"]] = hash["id"]
}
}
puts login_to_id["XXX"] # => 66939

Get parent node based on some condition in ruby

i have a hash like below.
prop = {"Pets"=>[]},
{"Misc"=>["HOA Frequency: (C101)"], "photos"=>nil},
{"Legal and finance"=>["HOA fee: $300.0"], "photos"=>nil}
I need to get Legal and finance nodes based on some condition.
I tried like below.
prop.find { |feature| feature.keys.include?("Legal and finance") }
But sometimes HOA fee will be under different node. It might be in "Finance" or "Legal and Finance" or "Home Finance" like
{"Finance"=>["HOA fee: $300.0"], "photos"=>nil} or
{"Home Finance"=>["HOA fee: $300.0"], "photos"=>nil}
So i need to get that complete node by checking whether any node contains text as "HOA Fee" as value.
prop.find do |feature|
feature.values.flatten.compact.any? do |value|
value.include?("HOA Fee")
end
end
This is a very messy data structure, however.
I would strongly advise you to refactor the code to store data in well-defined objects, not hashes of hashes of arrays...
I would do something like this:
prop.find { |hash| hash.keys.any? { |key| key.downcase.include?('finance') } }
#=> { "Legal and finance" => ["HOA fee: $300.0"], "photos" => nil }

Access variable hash depth values with square brackets notation

Given this hash:
hash1= { node1: { node2: { node3: { node4: { node5: 1 } } } } }
We access inside nodes with square brackets like this:
hash1[:node1][:node2][:node3][:node4]
Now I have a hash that I know will always be nested as it is an XML response from a SOAP webservice, but neither the depth of the hash nor the names of the nodes stay the same. So it would be nice if I could ask the user of my application for the hash depth and store it in a variable. And then be able to do hash1[:hash_depth] and achieve the same result as above.
I have accomplished what I want by the following code:
str = 'node1,node2,node3,node4'
str_a = str.split(',')
hash_copy = hash1
str_a.each { |s| hash_copy = hash_copy.[](s.to_sym) }
hash_copy
=> {:node5=>1}
hash1[:node1][:node2][:node3][:node4]
=> {:node5=>1}
that is asking the user to enter the hash depth separated by commas, store it in a string, split it, make an array, clone the original hash, go down each level and modify the hash till I get to the desired node. Is there a way to do it with the square brackets notation and using a variable to store the depth without modifying the hash or needing to clone it?
Edit:
someone answered with the following (can't see his post anymore???)
hash_depth="[:node1][:node2][:node3][:node4]"
eval "hash1#{hash_depth}"
Although eval does everything you need, there is another approach, since you already have the working code for comma-separated list:
hash_depth="[:node1][:node2][:node3][:node4]"
csh = hash_depth.gsub(/\A\[:|\]\[:|\]\Z/, { '][:' => ',' })
#⇒ "node1,node2,node3,node4"
And now you are free to apply your existing function to csh.
If this is a webapp, I think you should prepare a list of short textareas, which starts with a single text item, and the user can keep adding a new item to the list by clicking on a button. The areas will be filled by the user, and will be sent.
Then, you will probably receive this through some serialized form. You decode this to get an array of strings:
str_a = ["node1", "node2", "node3", "node4"]
and you can reach the inner element by doing:
str_a.inject(hash1){|h, s| h[s.to_sym]} #=> {:node5 => 1}

How do I extract a value from this Ruby hash?

I'm using the Foursquare API, and I want to extract the "id" value from this hash
[{"id"=>"4fe89779e4b09fd3748d3c5a", "name"=>"Hitcrowd", "contact"=>{"phone"=>"8662012805", "formattedPhone"=>"(866) 201-2805", "twitter"=>"hitcrowd"}, "location"=>{"address"=>"1275 Glenlivet Drive", "crossStreet"=>"Route 100", "lat"=>40.59089895083072, "lng"=>-75.6291255071468, "postalCode"=>"18106", "city"=>"Allentown", "state"=>"Pa", "country"=>"United States", "cc"=>"US"}, "categories"=>[{"id"=>"4bf58dd8d48988d125941735", "name"=>"Tech Startup", "pluralName"=>"Tech Startups", "shortName"=>"Tech Startup", "icon"=>"https://foursquare.com/img/categories/shops/technology.png", "parents"=>["Professional & Other Places", "Offices"], "primary"=>true}], "verified"=>true, "stats"=>{"checkinsCount"=>86, "usersCount"=>4, "tipCount"=>0}, "url"=>"http://www.hitcrowd.com", "likes"=>{"count"=>0, "groups"=>[]}, "beenHere"=>{"count"=>0}, "storeId"=>""}]
When I try to extract it by using ['id'], I get this error can't convert Symbol into Integer. How do I extract the value using ruby? Also, how do I do this for multiple hashes extracting the "id" value each time?
Please pardon my inexperience. Thanks!
It's wrapped in an array, that's what the [ and ] mean on the start and end. But it also looks like this array only one object in it, which is the hash you really want.
So assuming you want the first object in this array:
mydata[0]['id'] # or mydata.first['id'] as Factor Mystic suggests
But usually when an API returns an Array there is a reason (it might return many results instead of just one), and naively plucking the first item from it my not be what you want. So be sure you are getting the kind of data you really expect before hard coding this into your application.
For multiple hashes, if you want to do something with the id (run a procedure of some kind) then
resultsArray.each do |person|
id = person["id"] #then do something with the id
end
If you want to just get an array containing the ids then
resultsArray.map{|person| person["id"]}
# ["4fe89779e4b09fd3748d3c5a", "5df890079e4b09fd3748d3c5a"]
To just grab the one item from the array, see Alex Wayne's answer
To get an array of ids, try: resultsArray.map { |result| result["id"] }

Resources