Getting data from result set and transposing to file Ruby - ruby

I have result set with some information about a client like
[{name: John, age:20, state:y, city:w, country:x,...},{name:.....}]
Now I want to loop through this list getting only name, state, and city. And create a file with this information in the following format
name | age | city
How can I do this? I thought about adding to a 3d list and then transposing to csv. But I don't like this idea.

CSV is not that hard to do (using the test data from #dawg):
require 'csv'
a = [{name: "John", age:20, state:"ID", city:"Boise", country:"USA"},
{name: "Bob", age:20, state:"CA", city:"LA", country:"USA"}
]
File.open("test.csv","w") do |f|
a.each{|hsh| f << hsh.values_at(:name, :age, :city).to_csv}
end

Given:
a=[{name: "John", age:20, state:"ID", city:"Boise", country:"USA"},
{name: "Bob", age:20, state:"CA", city:"LA", country:"USA"}
]
headers=[:name, :age, :city]
You can do:
> a.map{|hsh| hsh.slice(*headers) }
=>
[{:name=>"John", :age=>20, :city=>"Boise"},
{:name=>"Bob", :age=>20, :city=>"LA"}]
And if you want that format:
puts headers.join(" | ")
a.map{|hsh| hsh.slice(*headers) }.
each{|hsh| puts hsh.values.join(" | ")}
Prints:
name | age | city
John | 20 | Boise
Bob | 20 | LA

Related

"Display information for each person"

I can't get the code to display the information about the people correctly. I'm able to list the names but not the ages.
my_group = ["person_1", "person_2", "person_3"]
person_1 = {name: "erik", gender: "male", age: 26}
person_2 = {name: "erika", gender: "female", age: 26}
person_3 = {name: "erka", gender: "alpha", age: 27}
my_group.each do |name|
my_group.each do |age|
puts "Hello, #{name} is about #{age} years old"
end
puts "Hello, #{name} is about #{age} years old"
end
I don't get an error code, but it does not list the age with the names.
Actually your my_group contains only Strings. If you want to include persons variable you may include them as variables (without ""):
person_1 = {name: "erik", gender: "male", age: 26}
person_2 = {name: "erika", gender: "female", age: 26}
person_3 = {name: "erka", gender: "alpha", age: 27}
my_group = [ person_1, person_2, person_3 ]
Then you can use Array#each method to loop on each person into this array.
my_group.each do |person|
puts "Hello, #{person[:name]} is about #{person[:age]} years old"
end
Also note that each person is a Hash. If you want to access to age property of person_1, for example, you have to do this:
puts person_1[:name]

Can't name fields using hirb in Ruby

I know my question is pretty simple, but I can't manage to find an answer on the internet.
I have a hash called sorted_frequency. I want to output it as a table using the gem hirb. At the time I just have been able to print the hash under the default field names (0, 1). So it looks like that:
0 1
wordA ntimes
wordB mtimes
wordC rtimes
I'd like to rename the field names so it would be something like this:
words number of times
wordA ntimes
wordB mtimes
wordC rtimes
My actual code is this:
#needs to install 'docx' and 'hirb' gems
require 'docx'
require 'hirb'
doc = Docx::Document.open('monografia.docx')
text_listed = doc.to_s.downcase.split(" ")
forbidden_list = ["o", "si", "em", "ha", "no", "és", "amb", "i", "/","el",
"la", "els","les", "l'", "lo", "los", "en", "n'", "na", "es", "ets", "s'",
"sa", "so", "ses", "sos", "un", "una", "unes", "uns", "a", "que", "s'",
"al", "de","del", "per", "ens", "als", "com"]
clean_text= text_listed - forbidden_list
frequency = Hash.new 0
clean_text.each { |word| frequency[word] += 1 }
sorted_frequency = Hash[frequency.sort_by{ | word, times | -times }[0..20]]
puts Hirb::Helpers::AutoTable.render(sorted_frequency)
Again, I'm sorry if this a newbie question
EDIT:
As all my code has been asked, I'll explain it. It opens a docx document with the help of a gem called 'docx'. After that, it splits the doc by spaces and creates an array. After that, I remove some words I don't want to count (those included in the forbidden_list). Then I create a hash where the key is the word, and the value is the number of times that word appears in the docx. After that, I sort that hash and output using the gem 'hirb. The problem is I just don't know how to name the fields of the table created. I hope someone can help me.
According to the hirb docs, AutoTable.render() takes an argument which can be an array_of_arrays or an array_of_hashes. But because your hash argument worked, I looked at your table output, and I decided to try to merely add an option to change the column names that you got:
require 'hirb'
freqs = {
'go' => 3,
'no' => 4,
'to' => 1
}
puts Hirb::Helpers::AutoTable.render(
freqs,
fields: [1, 0], #Specify which fields to include in the table and their order.
#For a row that is an array, the field names are the integers 0, 1, 2, etc.
#For a row that is a hash, the field names are the keys.
headers: {0 => 'Word', 1 => 'Frequency'}, #Convert the field names to something more desirable for the column headers
description: false #Get rid of "3 rows in set" following the table
)
--output:--
+-----------+------+
| Frequency | Word |
+-----------+------+
| 3 | go |
| 4 | no |
| 1 | to |
+-----------+------+
It worked. What must be happening is: AutoTable.render() expects an array--either an array_of_arrays or an array_of_hashes--and if the method doesn't get an array as an argument, it calls to_a() on the argument. Take a look at what happens to your hash:
~/ruby_programs$ irb
2.4.0 :001 > freqs = {'go' => 3, 'no' => 4, 'to' => 1}
=> {"go"=>3, "no"=>4, "to"=>1}
2.4.0 :002 > freqs.to_a
=> [["go", 3], ["no", 4], ["to", 1]]
There's the array_of_arrays that AutoTable.render() needs. Rearranging a little, the array_of_arrays looks like this:
index 0 index 1
| |
[ V V
["go", 3], #row array
["no", 4],
["to", 1]
]
For an array_of_arrays, the column headers in the table are the index positions in each row array. The options for AutoTable.render() let you specify which columns/index positions to include in the table and their order, and the options allow you to convert the column headers to something more desirable.
Here's a more general example:
require 'hirb'
require 'pp'
data = [
['go', 1, '1/12/18'],
['to', 4, '1/24/18'],
['at', 2, '1/28/18']
]
puts Hirb::Helpers::AutoTable.render(
data,
fields: [2, 0], #Specify the index positions in each row array to include in the table and their column order in the table
headers: {0 => 'Word', 2 => 'Date'}, #Convert the column headers to something more desirable
description: false #Get rid of "3 rows in set" following the table
)
--output:--
+---------+------+
| Date | Word |
+---------+------+
| 1/12/18 | go |
| 1/24/18 | to |
| 1/28/18 | at |
+---------+------+
====
require 'hirb'
require 'pp'
freqs = {
'go' => 3,
'no' => 4,
'to' => 1
}
col_names = %w[word count]
new_freqs = freqs.map do |key, val|
{col_names[0] => key, col_names[1] => val}
end
pp new_freqs
puts Hirb::Helpers::AutoTable.render(
new_freqs,
fields: ['word', 'count'], #Specify which keys to include in table and their column order.
headers: {'word' => 'Good Word', 'count' => 'Frequency'}, #Convert keys to more desirable headers.
description: false #Get rid of "3 rows in set" following the table
)
--output:--
[{"word"=>"go", "count"=>3},
{"word"=>"no", "count"=>4},
{"word"=>"to", "count"=>1}]
+-----------+-----------+
| Good Word | Frequency |
+-----------+-----------+
| go | 3 |
| no | 4 |
| to | 1 |
+-----------+-----------+
====
require 'hirb'
require 'pp'
freqs = {
'go' => 3,
'no' => 4,
'to' => 1
}
col_names = %i[word count]
new_freqs = freqs.map do |key, val|
{col_names[0] => key, col_names[1] => val}
end
pp new_freqs
puts Hirb::Helpers::AutoTable.render(new_freqs)
--output:--
[{:word=>"go", :count=>3}, {:word=>"no", :count=>4}, {:word=>"to", :count=>1}]
+-------+------+
| count | word |
+-------+------+
| 3 | go |
| 4 | no |
| 1 | to |
+-------+------+
3 rows in set
===
require 'hirb'
require 'pp'
data = {
'first' => 'second',
'third' => 'fourth',
'fifth' => 'sixth'
}
col_names = %i[field1 field2]
new_data = data.map do |key, val|
{col_names[0] => key, col_names[1] => val}
end
pp new_data
puts Hirb::Helpers::AutoTable.render(new_data)
--output:--
[{:field1=>"first", :field2=>"second"},
{:field1=>"third", :field2=>"fourth"},
{:field1=>"fifth", :field2=>"sixth"}]
+--------+--------+
| field1 | field2 |
+--------+--------+
| first | second |
| third | fourth |
| fifth | sixth |
+--------+--------+
3 rows in set
=====
require 'hirb'
data = [
{field1: 'first', field2: 'second'},
{field1: 'third', field2: 'fourth'},
{field1: 'fifth', field2: 'sixth'}
]
puts Hirb::Helpers::AutoTable.render(data)
--output:--
+--------+--------+
| field1 | field2 |
+--------+--------+
| first | second |
| third | fourth |
| fifth | sixth |
+--------+--------+
3 rows in set

Extract Hash values using Hash#dig

h = {
users: {
u_548912: {
name: "John",
age: 30
},
u_598715: {
name: "Doe",
age: 30
}
}
}
Given a hash like above, say I want to get user John, I can do
h[:users].values.first[:name] # => "John"
In Ruby 2.3 use Hash#dig can do the same thing:
h.dig(:users, :u_548912, :name) # => "John"
But given that the u_548912 is just a random number(no way to know it before hand), is there a way to get the information still using Hash#dig?
You can, of course, pass an expression as an argument to #dig:
h.dig(:users, h.dig(:users)&.keys&.first, :name)
#=> John
Extract the key if you want more legibility, at the cost of lines of code:
first_user_id = h.dig(:users)&.keys&.first
h.dig(:users, first_user_id, :name)
#=> John
Another option would be to chain your #dig method calls. This is shorter, but a bit less legible.
h.dig(:users)&.values&.dig(0, :name)
#=> John
I'm afraid there is no "neater" way of doing this while still having safe navigation.

ruby roo puts column from parsing

I'm total newbie in programming...thanks in advance for all those who will answer me..
I'm trying to print the columns starting from a search. Actually my excel is composed like this:
| | Header | Header | Header | Header |
|Header|Server 1|Server 2|Server 3|Server 4|
|Header| Data | Data | Data | Data |
|Header| Data | Data | Data | Data |
|Header| Data | Data | Data | Data |
This is my code but the output is not what I'm looking for....
fo_set_parse = xls.parse(:header_search => ['Server'], :clean => true)
fo_set_parse.each do |row|
row.each do |key,value|
if value != nil
puts "#{value}"
end
end
end
I'd like to print in the same excel style starting from a "Server" search..The number of "Server" change every time, so I'can't use something like
1.upto(xls.last_column) do |col|
server1 = xls.cell(2,col)
server2 = xls.cell(3,col)
server3 = xls.cell(4,col)
server4 = xls.cell(5,col)
puts "#{server1}\t #{server2}\t #{server3}\t #{server4}\t"
end
Any help?
I'm not familiar with roo (and at a quick glance, it doesn't seem like there are many examples to pull from), but how about:
data_row_definitions = {
:data_a => 2, # i.e. data_a is stored in row 2 for all servers
:data_b => 3,
:data_c => 4,
}
server_columns = 2.upto(xls.last_column) # or whatever columns the servers are listed
server_columns.map do |server_col|
data_for_server = Hash[
data_row_definitions.map do |data_name, row|
cell_value = xls.cell(row, server_col)
[data_name, cell_value]
end
]
end
With a table like:
| |Server 1|Server 2|Server N|
| dataA | 1| 3| 7|
| dataB | 10| 35| 14|
| dataC | 100| 95| 28|
(I would imagine) You would get the data structure:
[
{
"dataA" => 1,
"dataB" => 10,
"dataC" => 100,
},
{
"dataA" => 3,
"dataB" => 35,
"dataC" => 95,
},
{
"dataA" => 7,
"dataB" => 14,
"dataC" => 28,
},
]
And should work for however many N servers/columns you have due to every sever/column being enumerated in the server_columns variable.

One-liner to change multiple strings, upcase, downcase etc?

Hey first day coding ruby and I'm wondering if there is any method to changing multiple variables in one line for instance.
first_name = "JOHN"
last_name = "DOE"
[first_name, last_name] = [first_name, last_name].downcase
Output : john doe
Thanks
To change the values, use map:
first_name = "JOHN"
last_name = "DOE"
first_name, last_name = [first_name, last_name].map &:downcase
p first_name # => john
One solution could be to use the ! version of the downcase method
[first_name, last_name].each {|str| str.downcase!}
which can also be written :
[first_name, last_name].each(&:downcase!)
You can also use "collect".
[first_name, last_name].collect(&:downcase)
which returns
=> ["john", "doe"]
and if you want to join them together as a full name, you can use "join".
[first_name, last_name].collect(&:downcase).join(' ')
which returns
=> "john doe"

Resources