How do I convert this hash to an array of hashes? - arrays

I have this hash below called disciplines:
disciplines = {"Architecture"=>"architecture", "Auditing"=>"auditing", "Consulting"=>"consulting", "Delivery"=>"delivery", "Development"=>"development", "Engineering"=>"engineering", "Environment / IT"=>"environment", "Graphic Design"=>"graphic_design", "Management"=>"management", "Requirements"=>"requirements", "Research"=>"research", "Support"=>"support", "System Design"=>"system_design", "Test & Eval"=>"test_and_evaluation", "Writing"=>"writing"}
And I want to convert it into an array of hashes that looks like this:
[{"name"=>"Architecture", "value"=>"architecture"}, {"name"=>"Auditing", "value"=>"auditing"}, {"name"=>"Consulting", "value"=>"consulting"}, {"name"=>"Delivery", "value"=>"delivery"}, {"name"=>"Development", "value"=>"development"}, {"name"=>"Engineering", "value"=>"engineering"}, {"name"=>"Environment / IT", "value"=>"environment"}, {"name"=>"Graphic Design", "value"=>"graphic_design"}, {"name"=>"Management", "value"=>"management"}, {"name"=>"Requirements", "value"=>"requirements"}, {"name"=>"Research", "value"=>"research"}, {"name"=>"Support", "value"=>"support"}, {"name"=>"System Design", "value"=>"system_design"}, {"name"=>"Test & Eval", "value"=>"test_and_evaluation"}, {"name"=>"Writing", "value"=>"writing"}]
So I just want to take each key-value pair in the first hash and map it to a new hash where the key is now a value for name and the value is now a value for value and put them all in an array of hashes

You can simply do:
disciplines.map{ |k, v| { 'name' => k, 'value' => v } }
to achieve that.
Here's a demo: http://ideone.com/DBU3Ck

You can also do in this way:
array_of_hashes = disciplines.keys.inject([]) do |arr_of_hsh, item|
arr_of_hsh << ({name: item.downcase,value: item.capitalize})
end
Output will be look like this:
# array_of_hashes => [{:name=>"architecture", :value=>"Architecture"}, {:name=>"auditing", :value=>"Auditing"}, {:name=>"consulting", :value=>"Consulting"}, {:name=>"delivery", :value=>"Delivery"}, {:name=>"development", :value=>"Development"}, {:name=>"engineering", :value=>"Engineering"}, {:name=>"environment / it", :value=>"Environment / it"}, {:name=>"graphic design", :value=>"Graphic design"}, {:name=>"management", :value=>"Management"}, {:name=>"requirements", :value=>"Requirements"}, {:name=>"research", :value=>"Research"}, {:name=>"support", :value=>"Support"}, {:name=>"system design", :value=>"System design"}, {:name=>"test & eval", :value=>"Test & eval"}, {:name=>"writing", :value=>"Writing"}]

Related

putting a hash value into an array in ruby

I am trying to insert a list of account numbers into an array from an json return, I turned the json return into an hash, but I cannot for some reason insert the values into an array. I checked the hash locations on irb, and it gets the account number, for an example the location my_hash["aws_accounts"][0]["owner_id"] will get me the first account number and my_hash["aws_accounts"][0]["status"]["level"] will get me the status of the first account.
I essentially want to iterate through all the accounts and store the account number if its respective status color is "yellow".
HERE IS MY CODE:
require 'json'
require 'rest-client'
j = RestClient.get 'https://chapi.cloudhealthtech.com/v1/aws_accounts?api_key=###&page=1&per_page=100'
my_hash = JSON.parse(j)
accnt_size = my_hash["aws_accounts"].size
intaccntsize = accnt_size.to_i
account_number_array = Array.new
x = 0
for accnt_iteration in x..intaccntsize do
puts accnt_iteration
if my_hash["aws_accounts"][accnt_iteration]["status"]["level"] == "yellow"
account_number_array.push(my_hash["aws_accounts"][accnt_iteration]["owner_id"])
end
end
HERE IS THE ERROR MESSAGE
in `block in <main>': undefined method `[]' for nil:NilClass (NoMethodError)
from C:/Users/----/Desktop/-----/ruby_aws_sdk.rb:12:in `each'
from C:/Users/------/Desktop/-------/ruby_aws_sdk.rb:12:in `<main>'
any suggestions will help. thanks.
The actual solution to your problem is to use the 3 dot range instead of 2 dot (3 dots is end-exclusive and 2 dots is end-inclusive) as seen in the following (this uses the same my_hash as my second code block below):
for x in 0..my_hash["aws_accounts"].size do
puts x
end
# 0
# 1
# 2
=> 0..2
my_hash["aws_accounts"][2]
=> nil
for x in 0...my_hash["aws_accounts"].size do
puts x
end
# 0
# 1
=> 0...2
my_hash["aws_accounts"][1]
=> {"owner_id"=>2, "status"=>{"level"=>"orange"}}
Instead of getting the number of accounts and trying to access them through their index, I would just iterate over the accounts on their own. Here's a quick sample, with what I believe (based on your description) are the relevant pieces of my_hash and your expected output.
my_hash = {
"aws_accounts" => [
{ "owner_id" => 1, "status" => { "level" => "yellow" } },
{ "owner_id" => 2, "status" => { "level" => "orange" } }
]
}
account_number_array = []
my_hash["aws_accounts"].each do |account|
if account["status"]["level"] == "yellow"
account_number_array << account["owner_id"]
end
end
puts account_number_array.inspect
# => [1]

Ruby - Array of Hashes to CSV

I have collected some data and saved it to an array of hashes in the form of:
info = [
{'Name' => 'Alex', 'Tel' => 999, 'E-mail' => "bla#bla.com"},
{'Name' => 'Ali', 'Tel' => 995, 'E-mail' => "ali#bla.com"}
# ...
]
But not all information is always there:
{'Name' => 'GuyWithNoTelephone', 'E-mail' => "poor#bla.com"}
I want to turn this information into a CSV file. Here is what I tried:
def to_csv (info)
CSV.open("sm-data.csv", "wb") do |csv|
csv << ["Name", "Tel", "E-mail"]
info.each do |person|
csv << person.values
When I try this, the format of the table is not correct, and say, if a telephone number is missing, then the e-mail of that person appears at telephone column.
How can I selectively write this info to the CSV file, i.e how can I tell to skip columns if a value is missing?
But sadly when I try this, the format of the table is not correct, and say, if a telephone number is missing, then the e-mail of that person appears at telephone column.
That's because you are omitting the telephone number in that case, providing just 2 of the 3 values. To fix this, you simply have to provide all 3 values, even if they don't exist in the hash:
csv << ['Name', 'Tel', 'E-mail']
info.each do |person|
csv << [person['Name'], person['Tel'], person['E-Mail']]
end
or via Hash#values_at:
csv << ['Name', 'Tel', 'E-mail']
info.each do |person|
csv << person.values_at('Name', 'Tel', 'E-Mail')
end
For:
{'Name' => 'GuyWithNoTelephone', 'E-mail' => "poor#bla.com"}
this results in:
csv << ['GuyWithNoTelephone', nil, 'poor#bla.com']
which generates this output: (note the two commas, denoting an empty field in-between)
"GuyWithNoTelephone,,poor#bla.com\n"
Try this,
def to_csv(csv_filename="sm-data.csv")
# Get all unique keys into an array:
keys = info.map(&:keys).inject(&:|)
CSV.open(csv_filename, "wb") do |csv|
csv << keys
info.each do |hash|
# fetch values at keys location, inserting null if not found.
csv << hash.values_at(*keys)
end
end
end
Simple as this:
path = "data/backtest-results.csv"
CSV.open(path, "wb") do |csv|
csv << ["Asset", "Strategy", "Profit"]
result_array.each do |p|
csv << p.map { |key, value| value }
end
end
use "(File.file?(path) ? "ab" : "wb")" rather than "wb" if you want to continue adding as the new data comes

Ruby - Filtering array of hashes based on another array

I am trying to filter an array of hashes based on another array. What's the best way to accomplish this? Here are the 2 brutes I've right now:
x=[1,2,3]
y = [{dis:4,as:"hi"},{dis:2,as:"li"}]
1) aa = []
x.each do |a|
qq = y.select{|k,v| k[:dis]==a}
aa+=qq unless qq.empty?
end
2) q = []
y.each do |k,v|
x.each do |ele|
if k[:dis]==ele
q << {dis: ele,as: k[:as]}
end
end
end
Here's the output I'm intending:
[{dis:2,as:"li"}]
If you want to select only the elements where the value of :dis is included in x:
y.select{|h| x.include? h[:dis]}
You can delete the nonconforming elements of y in place with with .keep_if
> y.keep_if { |h| x.include? h[:dis] }
Or reverse the logic with .delete_if:
> y.delete_if { |h| !x.include? h[:dis] }
All produce:
> y
=> [{:dis=>2, :as=>"li"}]
Yes use select, nonetheless here's another way which works:
y.each_with_object([]) { |hash,obj| obj << hash if x.include? hash[:dis] }

How to convert a 2D array to a value object in ruby

I have a 2D array:
a = [["john doe", "01/03/2017", "01/04/2017", "event"], ["jane doe", "01/05/2017", "01/06/2017", "event"]...]
I would like to convert it to a value object in ruby. I found how to do it with a hash Ruby / Replace value in array of hash in the second answer of this question but not a 2D array. I would like to assign the value at a[0][0] to an attribute named "name", a[0][1] to "date1", a[0][2] to "date2" and a[0][3] to "event".
This is something like what I'd like to accomplish although it is not complete and I dont know how to assign multiple indexes to the different attributes in one loop:
class Schedule_info
arrt_accessor :name, :date1, :date2, :event
def initialize arr
#I would like this loop to contain all 4 attr assignments
arr.each {|i| instance_variable_set(:name, i[0])}
This should be short and clean enough, without unneeded metaprogramming :
data = [["john doe", "01/03/2017", "01/04/2017", "event"],
["jane doe", "01/05/2017", "01/06/2017", "event"]]
class ScheduleInfo
attr_reader :name, :date1, :date2, :type
def initialize(*params)
#name, #date1, #date2, #type = params
end
def to_s
format('%s for %s between %s and %s', type, name, date1, date2)
end
end
p info = ScheduleInfo.new('jane', '31/03/2017', '01/04/2017', 'party')
# #<ScheduleInfo:0x00000000d854a0 #name="jane", #date1="31/03/2017", #date2="01/04/2017", #type="party">
puts info.name
# "jane"
schedule_infos = data.map{ |params| ScheduleInfo.new(*params) }
puts schedule_infos
# event for john doe between 01/03/2017 and 01/04/2017
# event for jane doe between 01/05/2017 and 01/06/2017
You can't store the key value pairs in array index. Either you need to just remember that first index of array is gonna have "name" and assign a[0][0] = "foo" or just use array of hashes for the key value functionality you want to have
2.3.0 :006 > a = []
=> []
2.3.0 :007 > hash1 = {name: "hash1name", date: "hash1date", event: "hash1event"}
=> {:name=>"hash1name", :date=>"hash1date", :event=>"hash1event"}
2.3.0 :008 > a << hash1
=> [{:name=>"hash1name", :date=>"hash1date", :event=>"hash1event"}]
2.3.0 :009 > hash2 = {name: "hash2name", date: "hash2date", event: "hash2event"}
=> {:name=>"hash2name", :date=>"hash2date", :event=>"hash2event"}
2.3.0 :010 > a << hash2
=> [{:name=>"hash1name", :date=>"hash1date", :event=>"hash1event"}, {:name=>"hash2name", :date=>"hash2date", :event=>"hash2event"}]
It sounds like you want to call the attribute accessor method that corresponds to each array value. You use send to call methods programmatically. So you need an array of the method names that corresponds to the values you have in your given array. Now, assuming the class with your attributes is called Data.
attrs = [:name, :date1, :date2, :event]
result = a.map do |e|
d = Data.new
e.each.with_index do |v, i|
d.send(attrs[i], v)
end
d
end
The value result is an array of Data objects populated from your given array.
Of course, if you control the definition of your Data object, the best things would be to give it an initialize method that takes an array of values.
Try this:
class Schedule_info
arrt_accessor :name, :date1, :date2, :event
def initialize arr
#name = []
#date1 = []
#date2 = []
#event = []
arr.each |i| do
name << i[0]
date1 << i[1]
date2 << i[2]
event << i[3]
end
end
end

How to extract hashes from an array of hashes based on array value

input_hash = [{"id"=>"123", "name"=>"ashly"}, {"id"=>"73", "name"=>"george"}, {"id"=>"175", "name"=>"nancy"}, {"id"=>"433", "name"=>"grace"}]
check = ["73", "175"]
output => "george, nancy"
I can guess 'select' can be used. But not very sure how it can select both values in the array
input_hash.map(&:values).to_h.values_at(*check).join(", ")
# => "george, nancy"
check.flat_map{|c| input_hash.select{|aa| aa["id"] == c}}.map{|a| a["name"]}.join(", ")
=> "george, nancy"
or
input_hash.select{|h| h["name"] if check.include? h["id"]}.map{|aa| aa["name"]}.join(", ")
=> "george, nancy"
input_hash.map { |hash| hash["name"] if check.include?(hash["id"]) }.compact
input_hash.select {|h| check.include?(h["id"])}.map {|h| h["name"]}.join(", ")
Try this:
def get_output_hash(input_hash, ids)
input_hash.each do |hash|
if ids.include?(hash["id"])
p hash["name"]
end
end
end
Call it like:-
input_hash = [{"id"=>"123", "name"=>"ashly"}, {"id"=>"73", "name"=>"george"}, {"id"=>"175", "name"=>"nancy"}, {"id"=>"433", "name"=>"grace"}]
get_output_hash(input_hash, ["73", "175"])

Resources