Ruby: Shorthand for associative hash/array from mysql2 result set - ruby

So, new to ruby and struggling on this:
images table - 2 columns, filename and md5
using the mysql2 extension
images = Hash.new
results = client.query("SELECT * FROM images").each do |row|
images[row['filename']] = row['md5']
end
i'd like to just do this automatically, it seems pointless to loop through to make a hash - I think that I have missed something ?

You can try following
images = Hash[*Image.all.map{ |i| [i.filename, i.md5] }.flatten]

Related

Parsing sqlite3 query responses in ruby

I'm trying to read and parse an sqlite3 query in Ruby using the sqlite3 gem. This db already exists on my machine.
I'm opening the db with
db = SQLite3::Database.new "/path to/database.sqlite"
The I'm executing my particular query with
db.execute( "SELECT * FROM `ZSFNOTE` WHERE `ZTRASHED` LIKE '0'" ) do |row|
Now, based on my (limited) experience, I was hoping that this could be parsed like a JSON response, where I could call something like row["ZTITLE"]. However, those headers aren't available in my response, I can only get at what I'm looking for by guessing an integer, like row[19].
I know I'm not even scratching the surface of the sqlite3 gem, but couldn't find the answer to this in the docs. Any help would be much appreciated.
You can use #execute2 to get the headers.
require 'sqlite3'
db = SQLite3::Database.new(':memory:')
db.execute 'CREATE TABLE "examples" ("header" varchar(20), "value" integer(8))'
db.execute 'INSERT INTO examples(header, value) VALUES("example", 1)'
db.execute2('select * from examples')
# => [["header", "value"], ["example", 1]]
You can map the headers to the columns like so:
headers, *rows = db.execute2('select * from examples')
rows.map! do |row|
row.each_with_index.with_object({}) do |(col, i), o|
o[headers[i]] = col
end
end
rows.each do |row|
p row['header']
end
# => "example"

Create a Ruby Hash out of an xml string with the 'ox' gem

I am currently trying to create a hash out of an xml documen, with the help of the ox gem
Input xml:
<?xml version="1.0"?>
<expense>
<payee>starbucks</payee>
<amount>5.75</amount>
<date>2017-06-10</date>
</expense>
with the following ruby/ox code:
doc = Ox.parse(xml)
plist = doc.root.nodes
I get the following output:
=> [#<Ox::Element:0x00007f80d985a668 #value="payee", #attributes={}, #nodes=["starbucks"]>, #<Ox::Element:0x00007f80d9839198 #value="amount", #attributes={}, #nodes=["5.75"]>, #<Ox::Element:0x00007f80d9028788 #value="date", #attributes={}, #nodes=["2017-06-10"]>]
The output I want is a hash in the format:
{'payee' => 'Starbucks',
'amount' => 5.75,
'date' => '2017-06-10'}
to save in my sqllite database. How can I transform the objects array into a hash like above.
Any help is highly appreciated.
The docs suggest you can use the following:
require 'ox'
xml = %{
<top name="sample">
<middle name="second">
<bottom name="third">Rock bottom</bottom>
</middle>
</top>
}
puts Ox.load(xml, mode: :hash)
puts Ox.load(xml, mode: :hash_no_attrs)
#{:top=>[{:name=>"sample"}, {:middle=>[{:name=>"second"}, {:bottom=>[{:name=>"third"}, "Rock bottom"]}]}]}
#{:top=>{:middle=>{:bottom=>"Rock bottom"}}}
I'm not sure that's exactly what you're looking for though.
Otherwise, it really depends on the methods available on the Ox::Element instances in the array.
From the docs, it looks like there are two handy methods here: you can use [] and text.
Therefore, I'd use reduce to coerce the array into the hash format you're looking for, using something like the following:
ox_nodes = [#<Ox::Element:0x00007f80d985a668 #value="payee", #attributes={}, #nodes=["starbucks"]>, #<Ox::Element:0x00007f80d9839198 #value="amount", #attributes={}, #nodes=["5.75"]>, #<Ox::Element:0x00007f80d9028788 #value="date", #attributes={}, #nodes=["2017-06-10"]>]
ox_nodes.reduce({}) do |hash, node|
hash[node['#value']] = node.text
hash
end
I'm not sure whether node['#value'] will work, so you might need to experiment with that - otherwise perhaps node.instance_variable_get('#value') would do it.
node.text does the following, which sounds about right:
Returns the first String in the elements nodes array or nil if there is no String node.
N.B. I prefer to tidy the reduce block a little using tap, something like the following:
ox_nodes.reduce({}) do |hash, node|
hash.tap { |h| h[node['#value']] = node.text }
end
Hope that helps - let me know how you get on!
I found the answer to the question in my last comment by myself:
def create_xml(expense)
Ox.default_options=({:with_xml => false})
doc = Ox::Document.new(:version => '1.0')
expense.each do |key, value|
e = Ox::Element.new(key)
e << value
doc << e
end
Ox.dump(doc)
end
The next question would be how can i transform the value of the amount key from a string to an integer befopre saving it to the database

How can I convert this CSV to JSON with Ruby?

I am trying to convert a CSV file to JSON using Ruby. I am very, very, green when it comes to working with Ruby (or any language for that matter) so the answers may need to be dumbed down for me. Putting it in JSON seems like the most reasonable solution to me because I understand how to work with JSON when assigning variables equal to the attributes that come in the response. If there is a better way to do it, feel free to teach me.
My CSV is in the following format:
Header1,Header,Header3
ValueX,ValueY,ValueZ
I would like to be able to use the data to say something along the lines of this:
For each ValueX in Row 1 after the headers, check if valueZ is > ValueY. If yes, do this, if no do that. I understand how to do the if statement, just now how to parse out my information into variables/arrays.
Any ideas here?
require 'csv'
require 'json'
rows = []
CSV.foreach('a.csv', headers: true, converters: :all) do |row|
rows << row.to_hash
end
puts rows.to_json
# => [{"Header1":"ValueX","Header":"ValueY","Header3":"ValueZ"}]
Here is a first pointer:
require 'csv'
data = CSV.read('your_file.csv', { :col_sep => ',' }
Now you should have the data in data; you can test in irb.
I don't entirely understand the question:
if z > y
# do this
else
# do that
end
For JSON, you should be able to do JSON.parse().
I am not sure what target format JSON requires, probably a Hash.
You can populate your hash with the dataset from the CVS:
hash = Hash.new
hash[key_goes_here] = value_here

Why won't array take random, shuffle, limit, etc.?

In my Rails controller code I would like to randomly retrieve three of each content:
#content = Content.includes(:author).find(params[:id])
content_sub_categories = #content.subcategories
related_content = []
content_sub_categories.each do |sub_cat|
related_content << sub_cat.contents
end
#related_content = related_content.rand.limit(3)
rand.limit(3) isn't working, and the errors include:
undefined method `limit' for #<Array:0x007f9e19806bf0>
I'm familiar with Rails but still in the process of learning Ruby. Any help would be incredibly appreciated.
Perhaps it could be I am also rendering out the content in this way <%= #related_content %>?
I'm using:
Rails 3.2.14
Ruby 1.9.3
limit is a a method on ActiveRecord relations (that adds LIMIT X) to the SQL generated. However you have an array not a relation, hence the error.
The equivalent array method is take. You can of course combine both the shuffling and the limit into one step by using the sample method
If you want to pick 3 random elements, use Array#sample:
related_content.sample(3)
This should work:
related_content = []
content_sub_categories.each do |sub_cat|
related_content << sub_cat.contents.sample(3) # add 3 random elements
end
#related_content = related_content
Or without temporary variables using map:
#related_content = #content.subcategories.map { |cat| cat.contents.sample(3) }
Note that #related_content is an array of (3-element) arrays.
How is this ?
a = (1..10).to_a
p a.sample(3)
# >> [4, 10, 7]
Here's the final answer for finding the content id's subcategories, all of these subcategory's contents and displaying the content without repeats:
def show
#content = Content.includes(:author).find(params[:id])
related_content = #content.subcategories.pluck(:id)
#related_content = Content.joins(:subcategories).published.order('random()').limit(3).where(subcategories: { id: related_content}).where('"contents"."id" <> ?', #content.id)
end

`[]': can't convert String into Integer (TypeError) Ruby

I am trying to iterate over a JSON parsed hash table (that has nested Array's of hashes) and insert into a Text Table . The JSON parsed code that I am trying to iterate over is:
{"server"=>{"security_groups"=>[{"name"=>"default"}], "adminPass"=>"LhXEPMkYmqF7", "id"=>"82b7e32b-f62b-4106-b499-e0046250229f", "links"=>[{"href"=>"http://10.30.1.49:8774/v2/89fc0b9d984d49fba5328766e923958f/servers/82b7e32b-f62b-4106-b499-e0046250229f", "rel"=>"self"}, {"href"=>"http://10.30.1.49:8774/89fc0b9d984d49fba5328766e923958f/servers/82b7e32b-f62b-4106-b499-e0046250229f", "rel"=>"bookmark"}], "OS-DCF:diskConfig"=>"MANUAL"}}
The code I am using to iterate over the top is:
server_table = Text::Table.new do | t |
t.head = ['Server ID', 'Server URL', 'Admin Password']
end
response = JSON.parse(r)
response['server'].each do | serv_info |
server_table.rows << [["#{serv_info['id']}", "#{serv_info['links'][0]['href']}", "#{serv_info['adminPass']}"]]
end
puts server_table
I am getting the error:
/lib/get_token.rb:166:in `[]': can't convert String into Integer (TypeError)
from ./lib/get_token.rb:166:in `create_server'
from ./lib/get_token.rb:165:in `each'
from ./lib/get_token.rb:165:in `create_server'
If I individually use puts to print out each command they work fine, but the iteration does not. The commands that pull the correct info are:
puts response['server']['links'][0]['href']
puts response['server']['id']
puts response['server']['adminPass']
All 3 of those work, but if I try and iterate over them I get the string error. I know it has something to do with .each returning an Array of hashes but I do not fully understand why the PUTS command is working without issue in the script and also in IRB.
Any thoughts?
Each serv_info is a pair of a map represented as an array of 2 elements. Therefore everything after << in your code is just wrong.
The secret to avoid such mistakes is to stop trying to obfuscate your own code.
server_table.rows should contain all possible triples of server ID, link and a password.
response = # { "server" => ...}
server = response['server']
server_id = server['id']
link_infos = server['links']
admin_pass = server['adminPass']
link_infos.each do |link_info|
link = link_info['href']
server_table.rows << [server_id, link, admin_pass]
end
Update
We can easily use this code to process multiple servers
response = # [ {"server" => ...}, ...]
response.each do |server|
... # above code snippet goes here
# or you may extract it into a method and call it here
end
Also I want to mention that irb is really great for dealing with this kind of problems. It is a command line Ruby interpreter and it's great for prototyping. It prints out result of each statement you type and has an autocompletion to help you find required classes/methods. Instead of waiting several hours to get an SO answer to simple question you will get it using irb in a couple of minutes.
Perhaps you mean just
serv_info = response['server']
server_table.rows << [["#{serv_info['id']}", "#{serv_info['links'][0]['href']}", "#{serv_info['adminPass']}"]]
Since response['server'] is a hash not an array.
Instead of using:
server_table.rows << [["#{serv_info['id']}", "#{serv_info['links'][0]['href']}", "#{serv_info['adminPass']}"]]
Try:
server_table.rows += [["#{serv_info['id']}", "#{serv_info['links'][0]['href']}", "#{serv_info['adminPass']}"]]
Or:
server_table.rows << ["#{serv_info['id']}", "#{serv_info['links'][0]['href']}", "#{serv_info['adminPass']}"]

Resources