Ruby: 2 word hash key separated by space - ruby

What method is missing in my program to make the 2 worded states work like New Jersey and South Carolina and etc. I know by putting an underscore in between words would be easier but the user would also need to include underscore in their input which is not so cool. I would really appreciate any help. Thanks!
us_capitals = {
"Alabama"=>"Montgomery",
"Alaska" => "Juneau",
"Arizona" => "Phoenix",
"Arkansas" => "Little Rock",
"California" => "Sacramento",
"Colorado" => "Denver",
"Connecticut" => "Hartford",
"Delaware" => "Dover",
"Florida" => "Tallahassee",
"Georgia" => "Atlanta",
"Hawaii" => "Honolulu",
"Idaho" => "Boise",
"Illinois" => "Springfield",
"Indiana" => "Indianapolis",
"Iowa" => "Des Moines",
"Kansas" => "Topeka",
"Kentucky" => "Frankfort",
"Louisiana" => "Baton Rouge",
"Maine" => "Augusta",
"Maryland" => "Annapolis",
"Massachusetts" => "Boston",
"Michigan" => "Lansing",
"Minnesota" => "Saint Paul",
"Mississippi" => "Jackson",
"Missouri" => "Jefferson",
"Montana" => "Helena",
"Nebraska" => "Lincoln",
"Nevada" => "Carson City",
"New Hampshire" => "Concord",
"New Jersey" => "Trenton",
"New Mexico" => "Santa Fe",
"New York" => "Albany",
"North Carolina" => "Raleigh",
"North Dakota" => "Bismarck",
"Ohio" => "Columbus",
"Oklahoma" => "Oklahoma City",
"Oregon" => "Salem",
"Pennsylvania" => "Harrisburg",
"Rhode Island" => "Providence",
"South Carolina" => "Columbia",
"South Dakota" => "Pierre",
"Tennessee" => "Nashville",
"Texas" => "Austin",
"Utah" => "Salt Lake City",
"Vermont" => "Montpelier",
"Virginia" => "Richmond",
"Washington" => "Olympia",
"West Virginia" => "Charleston",
"Wisconsin" => "Madison",
"Wyoming" => "Cheyenne"
}
loop do
puts "Enter a state to lookup it's capital:"
state = gets.chomp.capitalize
puts us_capitals[state]
end

Capitalize only formats the first letter in the string, thus it is not working with strings that contain multiple words. You can try to split your strings by space, capitalize each word and then join it back:
state = gets.chomp.split(' ').map(&:capitalize).join(' ')
Demonstration
If you were using Rails, you could just use titleize:
gets.chomp.titleize

Suppose gets returns "west virginIa\n". Then
r = /[[:alpha:]]+/
state = gets.chomp.squeeze(' ').gsub(r) { |s| s.capitalize }
#=> "west virginIa\n".chomp.squeeze(' ').gsub(r) { |s| s.capitalize }
#=> "west virginIa".squeeze(' ').gsub(r) { |s| s.capitalize }
#=> "west virginia".gsub(r) { |s| s.capitalize }
#=> "West Virginia"
us_capitals[state]
#=> "Charleston"
If gets returns "West Virgin1a", we obtain
state = gets.chomp.squeeze(' ').gsub(r) { |s| s.capitalize }
#=> "West Virgin1a"
us_capitals[state]
#=> nil
Recall that String#capitalize converts the first letter of a string to a capital letter (if it is a letter), but it also converts all other letters in the string to lower case.

It seems like the simplest solution would be to make the hash keys lowercase and then downcase the user input.
us_capitals = {
# ...
"nebraska" => "Lincoln",
"nevada" => "Carson City",
"new hampshire" => "Concord",
"new jersey" => "Trenton",
# ...
}
loop do
puts "Enter a state to lookup it's capital:"
state = gets.chomp.downcase
puts us_capitals[state]
end

Can you add a helper method to convert user input?
#state = "new jersey"
state.gsub!(/\b[a-z]/, &:capitalize)
#state = "New Jersey"
Credit to Jordan, in the comments

Related

Create a nested HASH from a API Call doesn't work properly

I am new here and i hope that I'm doing everything right.
I also searched the Forum and with Googel, but I didn't find the answer. (Or I did not notice that the solution lies before my eyes. Then I'm sorry >.< .)
i have a problem and i dont exactly know what i am doing wrong at the moment.
I make a API request and get a big JSON back. It looks somehow like that:
"apps": [
{
"title": "XX",
... many more data
},
{
"title": "XX",
... many more data
},
{
"title": "XX",
... many more data
}
... and so on
]
After that i want to create a hash with the data i need, for example it should look like:
{
"APP_0" => {"Title"=>"Name1", "ID"=>"1234", "OS"=>"os"}
"APP_1" => {"Title"=>"Name2", "ID"=>"5678", "OS"=>"os"}
}
but the values in the hash that i create with my code looks like:
"APP_1", {"Title"=>"Name2", "ID"=>"5678", "OS"=>"os"}
dont now if this is a valid hash? And after that i want to iterate through the Hash and just output the ID. But I get an error (TypeError). What am i doing wrong?
require 'json'
require 'net/http'
require 'uri'
require 'httparty'
response = HTTParty.get('https://xxx/api/2/app', {
headers: {"X-Toke" => "xyz"},
})
all_apps_parse = JSON.parse(response.body)
all_apps = Hash.new
all_apps_parse["apps"].each_with_index do |app, i|
all_apps["APP_#{i}"] = {'Title' => app["title"],
'ID' => app["id"],
'OS' => app["platform"]}
end
all_apps.each_with_index do |app, i|
app_id = app["App_#{i}"]["id"]
p app_id
end
I hope someone can understand the problem and can help me :-). Thanks in advance.
Assuming the data looks something like this:
all_apps_parse = { "apps" => [
{
"title" => "Name1",
"id" => 1234,
"platform" => "os"
},
{
"title" => "Name2",
"id" => 5678,
"platform" => "os"
},
{
"title" => "Name3",
"id" => 1111,
"platform" => "windows"
}]
}
and with a little idea of what you want to achieve, here is my solution:
all_apps = Hash.new
all_apps_parse["apps"].each_with_index do |app, i|
all_apps["APP_#{i}"] = { 'Title' => app["title"],
'ID' => app["id"],
'OS' => app["platform"] }
end
all_apps
=> {"APP_0"=>{"Title"=>"Name1", "ID"=>1234, "OS"=>"os"}, "APP_1"=>{"Title"=>"Name2", "ID"=>5678, "OS"=>"os"}, "APP_2"=>{"Title"=>"Name3", "ID"=>1111, "OS"=>"windows"}}
all_apps.each do |key, value|
puts key # => e.g. "APP_0"
puts value['ID'] # => e.g. 1234
end
# Prints
APP_0
1234
APP_1
5678
APP_2
1111

How to organize hashes by property

Here is the hashes that will be processed:
{
"flatiron school bk" => {
:location => "NYC"
},
"flatiron school" => {
:location => "NYC"
},
"dev boot camp" => {
:location => "SF"
},
"dev boot camp chicago" => {
:location => "Chicago"
},
"general assembly" => {
:location => "NYC"
},
"Hack Reactor" => {
:location => "SF"
}
}
I need to organize these hashes by location, like this:
{ "NYC"=>["flatiron school bk", "flatiron school", "general assembly"],
"SF"=>["dev boot camp", "Hack Reactor"],
"Chicago"=>["dev boot camp chicago"]}
}
You can use each_with_object to combine in into new hash:
hash.each_with_object({}) do |(name, data), res|
(res[data[:location]] ||= []) << name
end
Explanation:
each_with_object
Iterates the given block for each element with an arbitrary object given, and returns the initially given object.
In this case name and data is key and value of each element in given hash.
In (res[data[:location]] ||= []) << name you get location, create array in result hash for given location (if it doesn't exist), then put key of input hash to it.

Adding hashes to hashes (Ruby)

I've tried adding hashes through #Hash.new with no success, now I am trying .merge as per the forums with limited success as well. I'm trying to add #rand(1..100) into [0] without going into the hash manually. Any ideas?
#age = Hash.new
#email = Hash.new
#age2 = rand(1..100)
people = [
{
"first_name" => "Bob",
"last_name" => "Jones",
"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"]
}
]
people[0].each do |w|
people.merge({:age => rand(1..100)})
puts "array 0 is #{w}"
end
puts p people
Assuming that's your structure, you do this:
people.each do |person|
person['age'] = rand(1..100)
end
You ideally want to use symbol-style keys instead. That would mean declaring them like this:
people = [
{
first_name: "Bob",
last_name: "Jones",
...
},
...
]
That way you access them like people[0][:first_name]. Your merged in hash uses symbol keys for :age. Remember in Ruby strings and symbols are not equivalent, that is 'bob' != :bob. You should use symbols for regular structures like this, strings for more arbitrary data.

Convert Array to JSON, with Same Key, in Ruby

I'm trying to parse an array of hashes, grab a value from a specific key, and output to json with one predefined key for each value - and I'm stuck.
Array:
[{:object_id=>"jon-59",
:name=>"jon-59-gw (8db8fcae-055a-4b35-9f8f-739b68c0bd5d)",
:client_handle=>nil,
:extended_attributes=>nil,
:appliances_summary=>
{:vm_version=>"5.5.3",
:vm_build_info=>"5.5.3-2135647"},
:hypervisor_assist=>false,
:allowed_actions=>
{:string=>
["Change Log Level",
"Edit Dns",
"Edit Syslog"]},
:edge_assist_id=>"0"},
{:object_id=>"jon-60",
:name=>"jon-60-gw (d63ddc45-gd3c-40c3-9046-e7afa996934a)",
:client_handle=>nil,
:extended_attributes=>nil,
:appliances_summary=>
{:vm_version=>"5.5.3",
:vm_build_info=>"5.5.3-2168697"},
:hypervisor_assist=>false,
:allowed_actions=>
{:string=>
["Change Log Level",
"Edit Dns",
"Edit Syslog"]},
:edge_assist_id=>"0"}]
Desired Output
{
"data":[
{ "{#JONNAME}":"jon-59-gw" },
{ "{#JONNAME}":"jon-60-gw"},
]
}
Where I'm at:
def jon_discover
jon_summary.sort_by { |jon| jon[:object_id] }.each do |jon|
name = jon[:name].slice!(/\A\S*/)
my_hash = {'{#JONNAME}' => name}
puts JSON.generate(my_hash)
end
end
The above returns:
{ "{#JONNAME}":"jon-59-gw" }
{ "{#JONNAME}":"jon-60-gw" }
But I don't know where to take it from here, or if I'm on the right track. How can I get this into the desired output?
Thanks, cheers!
This is too complex:
my_hash = {"{#JONNAME}" => "#{name}"}
Keep it simple:
my_hash = {JONNAME => name}
the data is for Zabbix low level discovery
Then use single-quotes instead of double quotes for the key and use the bare name for the value:
my_hash = {'{#JONNAME}' => name}
so it's more apparent that {# is not a typo.
Instead of:
"jon-60-gw (d63ddc45-gd3c-40c3-9046-e7afa996934a)".slice!(/\A\S*/) # => "jon-60-gw"
Use:
"jon-60-gw (d63ddc45-gd3c-40c3-9046-e7afa996934a)".split.first # => "jon-60-gw"
Putting it all together:
require 'json'
ary = [
{
:object_id => "jon-59",
:name => "jon-59-gw (8db8fcae-055a-4b35-9f8f-739b68c0bd5d)",
:client_handle => nil,
:extended_attributes => nil,
:appliances_summary =>
{
:vm_version => "5.5.3",
:vm_build_info => "5.5.3-2135647"
},
:hypervisor_assist => false,
:allowed_actions => {
:string => ["Change Log Level", "Edit Dns", "Edit Syslog"]
},
:edge_assist_id => "0"
},
{
:object_id => "jon-60",
:name => "jon-60-gw (d63ddc45-gd3c-40c3-9046-e7afa996934a)",
:client_handle => nil,
:extended_attributes => nil,
:appliances_summary => {
:vm_version => "5.5.3",
:vm_build_info => "5.5.3-2168697"
},
:hypervisor_assist => false,
:allowed_actions => {
:string => ["Change Log Level", "Edit Dns", "Edit Syslog"]
},
:edge_assist_id => "0"
}
]
Here's how to walk through the data:
data = ary.map{ |hash|
{
'{#JONNAME}' => hash[:name].split.first
}
}
Here's how to generate the JSON:
puts JSON[{'data' => data}]
# >> {"data":[{"{#JONNAME}":"jon-59-gw"},{"{#JONNAME}":"jon-60-gw"}]}
If you need it sorted:
puts JSON[{'data' => data.sort_by{ |s| s['{#JONNAME}'] }}]
# >> {"data":[{"{#JONNAME}":"jon-59-gw"},{"{#JONNAME}":"jon-60-gw"}]}

Multiple limit condition in mongodb

I have a collection in which one of the field is "type". I want to get some values of each type depending upon condition which is same for all the types. Like I want 2 documents for type A, 2 for type B like that.
How to do this in a single query? I am using Ruby Active Record.
Generally what you are describing is a relatively common question around the MongoDB community which we could describe as the "top n results problem". This is when given some input that is likely sorted in some way, how to get the top n results without relying on arbitrary index values in the data.
MongoDB has the $first operator which is available to the aggregation framework which deals with the "top 1" part of the problem, as this actually takes the "first" item found on a grouping boundary, such as your "type". But getting more than "one" result of course gets a little more involved. There are some JIRA issues on this about modifying other operators to deal with n results or "restrict" or "slice". Notably SERVER-6074. But the problem can be handled in a few ways.
Popular implementations of the rails Active Record pattern for MongoDB storage are Mongoid and Mongo Mapper, both allow access to the "native" mongodb collection functions via a .collection accessor. This is what you basically need to be able to use native methods such as .aggregate() which supports more functionality than general Active Record aggregation.
Here is an aggregation approach with mongoid, though the general code does not alter once you have access to the native collection object:
require "mongoid"
require "pp";
Mongoid.configure.connect_to("test");
class Item
include Mongoid::Document
store_in collection: "item"
field :type, type: String
field :pos, type: String
end
Item.collection.drop
Item.collection.insert( :type => "A", :pos => "First" )
Item.collection.insert( :type => "A", :pos => "Second" )
Item.collection.insert( :type => "A", :pos => "Third" )
Item.collection.insert( :type => "A", :pos => "Forth" )
Item.collection.insert( :type => "B", :pos => "First" )
Item.collection.insert( :type => "B", :pos => "Second" )
Item.collection.insert( :type => "B", :pos => "Third" )
Item.collection.insert( :type => "B", :pos => "Forth" )
res = Item.collection.aggregate([
{ "$group" => {
"_id" => "$type",
"docs" => {
"$push" => {
"pos" => "$pos", "type" => "$type"
}
},
"one" => {
"$first" => {
"pos" => "$pos", "type" => "$type"
}
}
}},
{ "$unwind" => "$docs" },
{ "$project" => {
"docs" => {
"pos" => "$docs.pos",
"type" => "$docs.type",
"seen" => {
"$eq" => [ "$one", "$docs" ]
},
},
"one" => 1
}},
{ "$match" => {
"docs.seen" => false
}},
{ "$group" => {
"_id" => "$_id",
"one" => { "$first" => "$one" },
"two" => {
"$first" => {
"pos" => "$docs.pos",
"type" => "$docs.type"
}
},
"splitter" => {
"$first" => {
"$literal" => ["one","two"]
}
}
}},
{ "$unwind" => "$splitter" },
{ "$project" => {
"_id" => 0,
"type" => {
"$cond" => [
{ "$eq" => [ "$splitter", "one" ] },
"$one.type",
"$two.type"
]
},
"pos" => {
"$cond" => [
{ "$eq" => [ "$splitter", "one" ] },
"$one.pos",
"$two.pos"
]
}
}}
])
pp res
The naming in the documents is actually not used by the code, and titles in the data shown for "First", "Second" etc, are really just there to illustrate that you are indeed getting the "top 2" documents from the listing as a result.
So the approach here is essentially to create a "stack" of the documents "grouped" by your key, such as "type". The very first thing here is to take the "first" document from that stack using the $first operator.
The subsequent steps match the "seen" elements from the stack and filter them, then you take the "next" document off of the stack again using the $first operator. The final steps in there are really justx to return the documents to the original form as found in the input, which is generally what is expected from such a query.
So the result is of course, just the top 2 documents for each type:
{ "type"=>"A", "pos"=>"First" }
{ "type"=>"A", "pos"=>"Second" }
{ "type"=>"B", "pos"=>"First" }
{ "type"=>"B", "pos"=>"Second" }
There was a longer discussion and version of this as well as other solutions in this recent answer:
Mongodb aggregation $group, restrict length of array
Essentially the same thing despite the title and that case was looking to match up to 10 top entries or greater. There is some pipeline generation code there as well for dealing with larger matches as well as some alternate approaches that may be considered depending on your data.
You will not be able to do this directly with only the type column and the constraint that it must be one query. However there is (as always) a way to accomplish this.
To find documents of different types, you would need to have some type of additional value that, on average distributed the types out according to how you want the data back.
db.users.insert({type: 'A', index: 1})
db.users.insert({type: 'B', index: 2})
db.users.insert({type: 'A', index: 3})
db.users.insert({type: 'B', index: 4})
db.users.insert({type: 'A', index: 5})
db.users.insert({type: 'B', index: 6})
Then when querying for items with db.users.find(index: {$gt: 2, $lt: 7}) you will have the right distribution of items.
Though I'm not sure this was what you were looking for

Resources