Create hash with keys are table names, values are column names - ruby

I would like to iterate an array that has a database table names, I would like to create a hash like this:
{"table1"=>[column1,...,columnN],"table2"=>[column1,...,columnM]...}
This is what I did so far:
arr_table_names = ['table1','table2','table3']
arr_table_names.each do |table|
rs = pg_conn.exec 'SELECT * FROM table WHERE Id=0'
column_names = rs.nfields
h = Hash.new{|hsh,key| hsh[key] = [] }
h[table].push ??? column_names ?? I don't know how in this line
end
I tried to use Sebastian's solution, but got a syntax error:
def check_tables_same_content(table1,table2)
result = %w[#{table1} #{table2}].each_with_object([]) do |table, arr|
arr << { table.to_sym => #pg_conn.exec("SELECT * FROM #{table} WHERE false").fields }
end
puts result2
end
check_tables_same_content('company','company2')
company.rb:21:in `exec': ERROR: syntax error at or near "#" (PG::SyntaxError)
LINE 1: SELECT * FROM #{table1} WHERE false
^
Note that I am not looking on how to return column names.

Use .map to create a new array and pass the block. This should work:
arr_table_names.map do |table|
rs = pg_conn.execute "SELECT * FROM #{table} WHERE Id=0"
{table => rs.fields}
end
Update, you wanted a hash with the table names as keys, and columns as values so:
arr_table_names = ['table1','table2','table3']
hash = {}
arr_table_names.each do |table|
rs = pg_conn.execute "SELECT * FROM #{table} WHERE Id=0"
hash[table] = rs.fields
end
Update 2, you want the row data as an array for each column(key) in the hash:
arr_table_names = ['table1','table2','table3']
hash = {}
arr_table_names.each do |table|
rows = pg_conn.execute "SELECT * FROM #{table}"
hash[table] = rows.entries.map(&:values)
end

Related

ruby - access array with hash key

I am struggling to understand how I can access an array with a hash key. In my code, I create a hash with keys and values. Now, I want to set the values in a Car class. Whenever I try to instantiate the Car, the argument expects Integer and not a String.
I am getting the following error: TypeError (no implicit conversion of String into Integer)
Here is my code:
class Car_maker
attr_accessor :car_maker
def initialize(car_maker)
#car_maker = car_maker
end
end
class Car_model < Car_maker
attr_accessor :km, :type, :transmission, :stock, :drivetrain, :status,
:fuel, :car_maker, :model, :year, :trim, :features
#total number of instances & array with car objects
##totalCars = 0
##catalogue = []
def initialize(km, type, transmission, stock, drivetrain, status, fuel, car_maker, model, year, trim, features)
super(car_maker)
#km = km
#type = type
#transmission = transmission
#stock = stock
#drivetrain = drivetrain
#status = status
#fuel = fuel
#model = model
#year = year
#trim = trim
#features = features
##totalCars += 1
end
def self.convertListings2Catalogue(line)
#Initialise arrays and use them to compare
type = ["Sedan", "coupe", "hatchback", "station", "SUV"]
transmission = ["auto", "manual", "steptronic"]
drivetrain = ["FWD", "RWD", "AWD"]
status = ["new", "used"]
car_maker = ["honda", "toyota", "mercedes", "bmw", "lexus"]
hash = Hash.new
#In this part, we hash the set of features using regex
copyOfLine = line
regex = Regexp.new(/{(.*?)}/)
match_array = copyOfLine.scan(regex)
match_array.each do |line|
hash["features"] = line
end
#Now, we split every comma and start matching fields
newStr = line[0...line.index('{')] + line[line.index('}')+1...line.length]
arrayOfElements = newStr.split(',')
arrayOfElements.each do |value|
if value.include?("km") and !value.include?("/")
hash["km"] = value
elsif type.include?(value)
hash["type"] = value
elsif transmission.include?(value.downcase)
hash["transmission"] = value
elsif value.include?("/") and value.include?("km")
hash["fuel economy"] = value
elsif drivetrain.include?(value)
hash["drivetrain"] = value
elsif status.include?(value.downcase)
hash["status"] = value
elsif /(?=.*[a-zA-Z])(?=.*[0-9])/.match(value) and !value.include?("km")
hash["stock"] = value
elsif car_maker.include?(value.downcase)
hash["carmaker"] = value
elsif /^\d{4}$/.match(value)
hash["year"] = value
elsif value.length == 2
hash["trim"] = value
else
if value.length > 2
hash["model"] = value
end
end
end
end
end
textFile = File.open('cars.txt', 'r')
textFile.each_line{|line|
if line.length > 2
result = Car_model.convertListings2Catalogue(line)
puts "Hash: #{result}"
carObj = Car_model.new(result["km"], result["type"], result["transmission"], result["stock"], result["drivetrain"],
result["status"], result["fuel"], result["carmaker"], result["model"], result["year"], result["trim"], result["features"])
###catalogue.push (carObj)
end
}
This line
result = Car_model.convertListings2Catalogue(line)
Doesn't return the hash object. It returns arrayOfElements since that's what the each method actually returns and the each method is the last method executed in the method (although there are hash assignments within it, it's only the last value that's returned unless you use an explicit return statement.
Just use the variable hash in the last line of the convertListing2Catalog method
if value.length > 2
hash["model"] = value
end
end
end
hash # < this is the last line of the method so it's the value that will be returned
end
end
If you think about it, there were several variables created in the method. There's no reason to expect that the contents of any specific variable such as hash would be returned, and ruby methods by default return the last executed command.

Rails add hash into existing hash

I would like to do smth like:
a = Hash.new
a = {:profile => #user}
a[:profile][:contacts] = Hash.new
a[:profile][:contacts] = #user.contacts.all
but I am getting the error can't write unknown attribute `contacts'.
#user is the result of a select from our database.
I need to create a hash with this structure
[:profile][:name] = boris
[:profile][:sex] = 1
[:propfile][:contacts] = here anotrher hash
The solution is convert to hash result after select from db using #user.as_json
a = Hash.new
a[:profile] = #user.as_json
a[:profile][:contacts] = #user.contacts.all

local variable vs instance variable Ruby initialize

I have a class in Ruby where I pass in a Hash of commodity prices. They are in the form
{"date (string)" => price (float), etc, etc}
and in the initialise method I convert the dates to Dates like so:
#data = change_key_format(dates)
But I notice that that method seems to change the original argument as well. Why is that? Here is the code:
def initialize(commodity_name, data)
puts "creating ...#{commodity_name}"
#commodity_name = commodity_name
#data = change_hash_keys_to_dates(data)
#dates = array_of_hash_keys(data)
puts data ######## UNCHANGED
#data = fix_bloomberg_dates(#data, #dates)
puts data ######## CHANGED -------------------- WHY???
#get_price_data
end
def fix_bloomberg_dates(data, dates)
#Fixes the bad date from bloomberg
data.clone.each do |date, price|
#Looks for obvious wrong date
if date < Date.strptime("1900-01-01")
puts dates[1].class
date_gap = (dates[1] - dates[2]).to_i
last_date_day = dates[1].strftime("%a %d %b")
last_date_day = last_date_day.split(" ")
last_date_day = last_date_day[0].downcase
#Correct the data for either weekly or daily prices
#Provided there are no weekend prices
if date_gap == 7 && last_date_day == "fri"
new_date = dates[1] + 7
data[new_date] = data.delete(date)
elsif date_gap == 1 && last_date_day == "thu"
new_date = dates[1] + 4
data[new_date] = data.delete(date)
else
new_date = dates[1] + 1
data[new_date] = data.delete(date)
end
end
end
return data
end
def change_hash_keys_to_dates(hash)
hash.clone.each do |k,v|
date = Date.strptime(k, "%Y-%m-%d")
#Transforms the keys from strings to dates format
hash[date] = hash.delete(k)
end
return hash
end
def array_of_hash_keys(hash)
keys = hash.map do |date, price|
date
end
return keys
end
Because of these lines:
data[new_date] = data.delete(date)
You're modifying the original data object. If you don't want to do this, create a copy of the object:
data2 = data.clone
and then replace all other references to data with data2 in your method (including return data2).

use array to iterate and parse other arrays CSV?

I've got a list of persons saved in an array and I want to loop a file with organizations looking for matches and save them but it keeps going wrong. I think I'm doing something wrong with the arrays.
This is exactly what I'm doing:
I have a list of persons in a file called 'personen_fixed.csv'.
I save that list into an array.
I have another file that also has the name of the people ("pers2"), but also three other interesting columns of data. I save the four columns into arrays.
I want to loop over the first array (the persons) and search for matches with the list of persons ("pers2").
If there is a match I want to save that row.
What I'm getting now is two rows of data, of which one is filled with ALL persons. See my code below. On the bottom i have some sample input data.
require 'csv'
array_pers1 = []
array_pers2 = []
array_orgaan = []
array_functie = []
array_rol = []
filename_1 = 'personen_fixed.csv'
CSV.foreach(filename_1, :col_sep => ";", :encoding => "windows-1251:utf-8", :return_headers => false) do |row|
array_pers1 << row[0].to_s
end
filename_2 = 'Functies_fixed.csv'
CSV.foreach(filename_2, :col_sep => ";", :encoding => "windows-1251:utf-8", :return_headers => false) do |row|
array_pers2 << row[1].to_s
array_orgaan << row[16].to_s
array_functie << row[17].to_s
array_rol << row[18].to_s
end
CSV.open("testrij.csv", "w") do |row|
row << ["rijnummer","link","ptext","soort_woonhuis"]
for rij in array_pers1
for x in 1...4426 do
if rij === array_pers2["#{x}".to_f]
pers2 = array_pers2["#{x}".to_f]
orgaan = array_orgaan["#{x}".to_f]
functie = array_functie["#{x}".to_f]
rol = array_rol["#{x}".to_f]
row << [pers2,orgaan,functie,rol]
else
pers2 = ""
orgaan = ""
functie = ""
rol = ""
end
end
end
end
input data for the first excel data (excel column name and first row of data):
person
someonesname
Input data for the second excel file:
person,organizationid,role,organization,function
someonesname,34971,member,americanairways,boardofdirectors
Since many of the people in the dataset have multiple jobs at different organizations, I want to save all them next to eachother (output I'm going for):
person,organization(1),function(1),role(1),organization(2),function(2),role(2) (max 5)
I don't understand the purpose of storing a single row from your Functies csv file in 4 separate arrays, and then combining them together later, so my answer doesn't tell you why your approach isn't working. Instead, I suggest a different approach that I believe is cleaner.
Building an array of names from the first file is ok. For the second file, I would store each row as an array and use a hash:
data = {
"name1 => ["name1", "orgaan1", "functie1", "rol1"],
"name2 => ["name2", "orgaan2", "functie2", "rol2"],
...
}
Building it might look like
data = {}
CSV.foreach(filename_2, :col_sep => ";", :encoding => "windows-1251:utf-8", :return_headers => false) do |row|
name = row[1]
orgaan = row[16]
functie = row[17]
rol = row[18]
data[name] = [name, orgaan, functie, rol]
end
Then you would iterate over your first array and keep all the arrays that match
results = []
for name in array_pers1
results << data[name] if data.include?(name)
end
On the other hand, if you don't want to use a hash and insist on using arrays (perhaps because names are not unique), I would still store them like
data = [
["name1", "orgaan1", "functie1", "rol1"],
["name2", "orgaan2", "functie2", "rol2"]
]
And then during your search step you would just iterate like
results = []
for name in array_pers1
for row in data
results << row if row[0] == name
end
end

Can't convert symbol to integer from hash table

Edit: The issue is being unable to get the quantity of arrays within the hash, so it can be, x = amount of arrays. so it can be used as function.each_index{|x| code }
Trying to use the index of the amount of rows as a way of repeating an action X amount of times depending on how much data is pulled from a CSV file.
Terminal issued
=> Can't convert symbol to integer (TypeError)
Complete error:
=> ~/home/tests/Product.rb:30:in '[]' can't convert symbol into integer (TypeError) from ~home/tests/Product.rub:30:in 'getNumbRel'
from test.rb:36:in '<main>'
the function is that is performing the action is:
def getNumRel
if defined? #releaseHashTable
return #releaseHashTable[:releasename].length
else
#releaseHashTable = readReleaseCSV()
return #releaseHashTable[:releasename].length
end
end
The csv data pull is just a hash of arrays, nothing snazzy.
def readReleaseCSV()
$log.info("Method "+"#{self.class.name}"+"."+"#{__method__}"+" has started")
$log.debug("reading product csv file")
# Create a Hash where the default is an empty Array
result = Array.new
csvPath = "#{File.dirname(__FILE__)}"+"/../../data/addingProdRelProjIterTestSuite/releaseCSVdata.csv"
CSV.foreach(csvPath, :headers => true, :header_converters => :symbol) do |row|
row.each do |column, value|
if "#{column}" == "prodid"
proHash = Hash.new { |h, k| h[k] = [ ] }
proHash['relid'] << row[:relid]
proHash['releasename'] << row[:releasename]
proHash['inheritcomponents'] << row[:inheritcomponents]
productId = Integer(value)
if result[productId] == nil
result[productId] = Array.new
end
result[productId][result[productId].length] = proHash
end
end
end
$log.info("Method "+"#{self.class.name}"+"."+"#{__method__}"+" has finished")
#productReleaseArr = result
end
Sorry, couldn't resist, cleaned up your method.
# empty brackets unnecessary, no uppercase in method names
def read_release_csv
# you don't need + here
$log.info("Method #{self.class.name}.#{__method__} has started")
$log.debug("reading product csv file")
# you're returning this array. It is not a hash. [] is preferred over Array.new
result = []
csvPath = "#{File.dirname(__FILE__)}/../../data/addingProdRelProjIterTestSuite/releaseCSVdata.csv"
CSV.foreach(csvPath, :headers => true, :header_converters => :symbol) do |row|
row.each do |column, value|
# to_s is preferred
if column.to_s == "prodid"
proHash = Hash.new { |h, k| h[k] = [ ] }
proHash['relid'] << row[:relid]
proHash['releasename'] << row[:releasename]
proHash['inheritcomponents'] << row[:inheritcomponents]
# to_i is preferred
productId = value.to_i
# this notation is preferred
result[productId] ||= []
# this is identical to what you did and more readable
result[productId] << proHash
end
end
end
$log.info("Method #{self.class.name}.#{__method__} has finished")
#productReleaseArr = result
end
You haven't given much to go on, but it appears that #releaseHashTable contains an Array, not a Hash.
Update: Based on the implementation you posted, you can see that productId is an integer and that the return value of readReleaseCSV() is an array.
In order to get the releasename you want, you have to do this:
#releaseHashTable[productId][n][:releasename]
where productId and n are integers. Either you'll have to specify them specifically, or (if you don't know n) you'll have to introduce a loop to collect all the releasenames for all the products of a particular productId.
This is what Mark Thomas meant:
> a = [1,2,3] # => [1, 2, 3]
> a[:sym]
TypeError: can't convert Symbol into Integer
# here starts the backstrace
from (irb):2:in `[]'
from (irb):2
An Array is only accessible by an index like so a[1] this fetches the second element from the array
Your return a an array and thats why your code fails:
#....
result = Array.new
#....
#productReleaseArr = result
# and then later on you call
#releaseHashTable = readReleaseCSV()
#releaseHashTable[:releasename] # which gives you TypeError: can't convert Symbol into Integer

Resources