Append row to csv file Ruby 1.9 CSV lib - ruby

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
}

Related

Read from a CSV file, multiply two columns, and then write back to the CSV file in ruby?

I have created csv file with values.I am able to read rows but don't know how to access individual values of a column.
require "csv"
CSV.open("file.csv", "w")
do |csv|
csv << ["val1", "val2","mul"]
csv << ["53", "27"]
csv<<["32","20"]
end
You probably need to ignore the header row if you have one. But the general idea is this:
CSV.open('dest.csv', 'w') do |csv|
csv << ["val1", "val2","mul"]
CSV.foreach('source.csv') do |row|
c1 = row[0]
c2 = row[1]
csv << [c1, c2, c1*c2]
end
end
If you have headers, you could do this:
CSV.open('dest.csv', 'w') do |csv|
csv << ["val1", "val2", "mul"]
CSV.foreach('source.csv', headers: true) do |row|
c1 = row['val1']
c2 = row['val2']
csv << [c1, c2, c1*c2]
end
end
You can use the one below for a non-ruby solution too:
awk -F "," '{print $1,$2,$1*$2}' source.csv > dest.csv

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

How can I save the array to CSV file?

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

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) ]

Changing field separator/delimiter in exported CSV using Ruby CSV

Is it possible to change the default field separator from comma to to some other character, e.g '|' for exporting?
Here's an example using a tab instead.
To a file:
CSV.open("myfile.csv", "w", {:col_sep => "\t"}) do |csv|
csv << ["row", "of", "CSV", "data"]
csv << ["another", "row"]
# ...
end
To a string:
csv_string = CSV.generate(:col_sep => "\t") 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
The previous CSV library was replaced with FasterCSV in Ruby 1.9.
require "csv"
output = CSV.read("test.csv").map do |row|
row.to_csv(:col_sep => "|")
end
puts output
CSV::Writer has a generate method, which accepts a separator string as argument.
#!/usr/bin/env ruby
# +++ ruby 1.8 version +++
require "csv"
outfile = File.open('csvout', 'wb')
CSV::Writer.generate(outfile, '|') do |csv|
csv << ['c1', nil, '', '"', "\r\n", 'c2']
end
outfile.close

Resources