How can I save the array to CSV file? - ruby

I have a webcrawl script, and a scraper script. When I integrate the two, I can get the array that I want from every webpage, but I can't save it to CSV file. I tried this code but it doesn't work:
CSV.open("scraper.csv", "wb") do |csv|
csv << ["date", "venue", "time", "race_number", "race_name", "track", "purse"]
csv << $race_data = [date, venue, time, race_number, race_name, track, purse]
end
Well, it did saves a CSV file, but the file just keeps on overwriting itself instead of making just one file-with-everything-on-it.

found an answer to this. The problem was my CSV.open is in "w" which overwrites the files with new data. So, from
CSV.open("scraper.csv", "wb") do |csv|
csv << ["date", "venue", "time", "race_number", "race_name", "track", "purse"]
csv << $race_data = [date, venue, time, race_number, race_name, track, purse]
end
i put it this way:
CSV.open("scraper.csv", "ab") do |csv|
csv << ["date", "venue", "time", "race_number", "race_name", "track", "purse"]
csv << $race_data = [date, venue, time, race_number, race_name, track, purse]
end

Related

Nested Ruby Hash to CSV

I have a nested hash as follow example:
{
"0001" => {
"All nodes" => [N001, N002, N003],
"All links" => [N001.1, N002.1, N003.1],
"Pumps" => [N001.2]
},
"0002" => {
"All nodes" => [N004, N005, N006],
"All links" => [N004.1, N005.1, N006.1],
"Pumps" => [N005.2]
},
"0003" => {
"All nodes" => [N007, N008, N009],
"All links" => [N007.1, N008.1, N009.1],
"Pumps" => [N007.2]
}
}
Under Nodes are stored information like coordinates, under Links are stored inverts, diameters and storage and under Pumps are stored On/Off levels and discharge.
I would like to know if you have any idea for how to export to CSV the information that are stored in the hash but in the right column (which will be the hash keys (0001, 0002 and 0003)).
As example, this is what I managed to make till now:
require 'CSV'
net=WSApplication.current_network
CSVsaveloc=WSApplication.file_dialog(false, "csv", "Comma Separated Variable File", "testexportcsv",false,true)
f = File.new(CSVsaveloc, "w")
CSV.open(CSVsaveloc,"wb") do |csv|
csv << Hash.keys
Hash.each do |key,values|
csv << x.mean
csv << y.mean
csv << diameter.min
csv << discharge.min
end
end
Now I'm getting the export like this:
0001,0002,0003
246164.2646
518466.7589
300mm
0.01
246181.6492
518444.1727
250mm
0.005
246171.5763
518509.8948
500mm
0.1
BUT, I would like to have it like this:
0001,0002,0003
246164.2646,246181.6492,246171.5763
518466.7589,518444.1727,518509.8948
300mm,250mm,500mm
0.01,0.005,0.1
From what I'm noticing, it seems that CSV inserts to a new line every time you run csv << . It might help to create an array for each line you're expecting, then append to CSV from there.
Expanding from the example code you gave earlier, maybe you can try this instead?
require 'CSV'
net=WSApplication.current_network
CSVsaveloc=WSApplication.file_dialog(false, "csv", "Comma Separated Variable File", "testexportcsv",false,true)
f = File.new(CSVsaveloc, "w")
# Create arrays
x_mean = []
y_mean = []
diameter_min = []
discharge_min = []
# Insert each value to corresponding array
Hash.each do |key, values|
x_mean << x.mean
y_mean << y.mean
diameter_min << diameter.min
discharge_min << discharge.min
end
# Insert each array to each line of CSV
CSV.open(CSVsaveloc,"wb") do |csv|
csv << Hash.keys
csv << x_mean
csv << y_mean
csv << diameter_min
csv << discharge_min
end

manipulating csv with ruby

I have a CSV from which I've removed the irrelevant data.
Now I need to split "Name and surname" into 2 columns by space but ignoring a 3rd column in case there are 3 names, then invert the order of the columns "Name and surname" and "Phone" (phone first) and then put them into a file ignoring the headers. I've never actually learned Ruby but I've played with Python 10 years ago. Can you help me? This is what I was able to do until now:
E.g.
require 'csv'
csv_table = CSV.read(ARGV[0], :headers => true)
keep = ["Name and surname", "Phone", "Email"]
new_csv_table = csv_table.by_col!.delete_if do |column_name,column_values|
!keep.include? column_name
end
new_csv_table.to_csv
Begin by creating a CSV file.
str =<<~END
Name and surname,Phone,Email
John Doe,250-256-3145,John#Doe.com
Marsha Magpie,250-256-3154,Marsha#Magpie.com
END
File.write('t_in.csv', str)
#=> 109
Initially, let's read the file, add two columns, "Name" and "Surname", and optionally delete the column, "Name and surname", without regard to column order.
First read the file into a CSV::Table object.
require 'csv'
tbl = CSV.read('t_in.csv', headers: true)
#=> #<CSV::Table mode:col_or_row row_count:3>
Add the new columns.
tbl.each do |row|
row["Name"], row["Surname"] = row["Name and surname"].split
end
#=> #<CSV::Table mode:col_or_row row_count:3>
Note that if row["Name and surname"] had equaled “John Paul Jones”, we would have obtained row["Name"] #=> “John” and row["Surname"] #=> “Paul”.
If the column "Name and surname" is no longer required we can delete it.
tbl.delete("Name and surname")
#=> ["John Doe", "Marsha Magpie"]
Write tbl to a new CSV file.
CSV.open('t_out.csv', "w") do |csv|
csv << tbl.headers
tbl.each { |row| csv << row }
end
#=> #<CSV::Table mode:col_or_row row_count:3>
Let's see what was written.
puts File.read('t_out.csv')
displays
Phone,Email,Name,Surname
250-256-3145,John#Doe.com,John,Doe
250-256-3154,Marsha#Magpie.com,Marsha,Magpie
Now let's rearrange the order of the columns.
header_order = ["Phone", "Name", "Surname", "Email"]
CSV.open('t_out.csv', "w") do |csv|
csv << header_order
tbl.each { |row| csv << header_order.map { |header| row[header] } }
end
puts File.read('t_out.csv')
#=> #<CSV::Table mode:col_or_row row_count:3>
displays
Phone,Name,Surname,Email
250-256-3145,John,Doe,John#Doe.com
250-256-3154,Marsha,Magpie,Marsha#Magpie.com

Ruby: How to add two lines at once to a csv?

I have a list of items and a script which generates two lines of csv for each item.
May I add two lines at once to csv generator? I want something like this:
CSV.generate do |csv|
items.each do |item|
csv << rows(item)
end
end
def rows(item)
return \
['value1', 'value2', 'value2'],
['value3', 'value4', 'value5']
end
But csv << can't receive two lines at once.
Now my the best code is:
CSV.generate do |csv|
items.each do |item|
rows(item).each { |row| csv << row }
end
end
Update: Now the best code without adding two line at once looks like:
CSV.generate do |csv|
items.
flat_map(&method(:rows)).
each(&csv.method(:<<))
end
CSV.generate do |csv|
csv << items.flat_map(&method(:rows))
end
Array#push or Array#append work the same way, and can take multiple arguments. Edit: As it turns out, CSV.generate yields a CSV object which has neither of those methods.
You can also do it like this:
CSV.generate do |csv|
items.each do |item|
r = rows(item)
csv << r[0] << r[1]
end
end

Output array to CSV in Ruby

It's easy enough to read a CSV file into an array with Ruby but I can't find any good documentation on how to write an array into a CSV file. Can anyone tell me how to do this?
I'm using Ruby 1.9.2 if that matters.
To a file:
require 'csv'
CSV.open("myfile.csv", "w") do |csv|
csv << ["row", "of", "CSV", "data"]
csv << ["another", "row"]
# ...
end
To a string:
require 'csv'
csv_string = CSV.generate do |csv|
csv << ["row", "of", "CSV", "data"]
csv << ["another", "row"]
# ...
end
Here's the current documentation on CSV: http://ruby-doc.org/stdlib/libdoc/csv/rdoc/index.html
If you have an array of arrays of data:
rows = [["a1", "a2", "a3"],["b1", "b2", "b3", "b4"], ["c1", "c2", "c3"]]
Then you can write this to a file with the following, which I think is much simpler:
require "csv"
File.write("ss.csv", rows.map(&:to_csv).join)
I've got this down to just one line.
rows = [['a1', 'a2', 'a3'],['b1', 'b2', 'b3', 'b4'], ['c1', 'c2', 'c3'], ... ]
csv_str = rows.inject([]) { |csv, row| csv << CSV.generate_line(row) }.join("")
#=> "a1,a2,a3\nb1,b2,b3\nc1,c2,c3\n"
Do all of the above and save to a csv, in one line.
File.open("ss.csv", "w") {|f| f.write(rows.inject([]) { |csv, row| csv << CSV.generate_line(row) }.join(""))}
NOTE:
To convert an active record database to csv would be something like this I think
CSV.open(fn, 'w') do |csv|
csv << Model.column_names
Model.where(query).each do |m|
csv << m.attributes.values
end
end
Hmm #tamouse, that gist is somewhat confusing to me without reading the csv source, but generically, assuming each hash in your array has the same number of k/v pairs & that the keys are always the same, in the same order (i.e. if your data is structured), this should do the deed:
rowid = 0
CSV.open(fn, 'w') do |csv|
hsh_ary.each do |hsh|
rowid += 1
if rowid == 1
csv << hsh.keys# adding header row (column labels)
else
csv << hsh.values
end# of if/else inside hsh
end# of hsh's (rows)
end# of csv open
If your data isn't structured this obviously won't work
If anyone is interested, here are some one-liners (and a note on loss of type information in CSV):
require 'csv'
rows = [[1,2,3],[4,5]] # [[1, 2, 3], [4, 5]]
# To CSV string
csv = rows.map(&:to_csv).join # "1,2,3\n4,5\n"
# ... and back, as String[][]
rows2 = csv.split("\n").map(&:parse_csv) # [["1", "2", "3"], ["4", "5"]]
# File I/O:
filename = '/tmp/vsc.csv'
# Save to file -- answer to your question
IO.write(filename, rows.map(&:to_csv).join)
# Read from file
# rows3 = IO.read(filename).split("\n").map(&:parse_csv)
rows3 = CSV.read(filename)
rows3 == rows2 # true
rows3 == rows # false
Note: CSV loses all type information, you can use JSON to preserve basic type information, or go to verbose (but more easily human-editable) YAML to preserve all type information -- for example, if you need date type, which would become strings in CSV & JSON.
Building on #boulder_ruby's answer, this is what I'm looking for, assuming us_eco contains the CSV table as from my gist.
CSV.open('outfile.txt','wb', col_sep: "\t") do |csvfile|
csvfile << us_eco.first.keys
us_eco.each do |row|
csvfile << row.values
end
end
Updated the gist at https://gist.github.com/tamouse/4647196
Struggling with this myself. This is my take:
https://gist.github.com/2639448:
require 'csv'
class CSV
def CSV.unparse array
CSV.generate do |csv|
array.each { |i| csv << i }
end
end
end
CSV.unparse [ %w(your array), %w(goes here) ]

Append row to csv file Ruby 1.9 CSV lib

Using Ruby 1.9 and CSV lib, I can't seem to append a row. The example in the documentation opens the file, and overwrites the row. What is the correct way to append rows to the document?
Example from documentation:
require 'csv'
CSV.open("path/to/file.csv", "wb") do |csv|
csv << ["row", "of", "CSV", "data"]
csv << ["another", "row"]
# ...
end
I think you can change the open to use ab:
CSV.open("t.csv", "ab") do |csv|
I will usually use the following to write to a csv file (Or any file)
File.open("filename", 'a+') {|f| f.write("datatowrite\n)}
File.open('filename', 'a'){ |outfile|
CSV::Writer.generate(outfile) do |csv|
csv << ['c1', nil, '', '"', "\r\n", 'c2']
end
}

Resources