31

I am having trouble writing columns to a csv file with Ruby. Below is my snippet of code.

 calc = numerator/denominator.to_f
 data_out = "#{numerator}, #{denominator}, #{calc}"
 File.open('cdhu3_X.csv','a+') do|hdr|
      hdr << ["numerator","denominator","calculation\n"] #< column header
          hdr << "#{data_out}\n"
 end

The code adds the column headers to every line and I only need it at the top of each column of data. I have searched here and other places but can't find a clear answer to how its done. Any help would be greatly appreciated.

Joe
  • 743
  • 5
  • 10
  • 26

4 Answers4

61

I would recommend to use the CSV-library instead:

require 'csv'

CSV.open('test.csv','w', 
    :write_headers=> true,
    :headers => ["numerator","denominator","calculation"] #< column header
  ) do|hdr|
  1.upto(12){|numerator|
    1.upto(12){ |denominator|
      data_out = [numerator, denominator, numerator/denominator.to_f]
      hdr << data_out
    }
  }
end

If you can't use the w option and you really need the a+ (e.g., the data isn't available all at once), then you could try the following trick:

require 'csv'

column_header = ["numerator","denominator","calculation"]
1.upto(12){|numerator|
  1.upto(12){ |denominator|
    CSV.open('test.csv','a+', 
        :write_headers=> true,
        :headers => column_header
      ) do|hdr|
          column_header = nil #No header after first insertion
          data_out = [numerator, denominator, numerator/denominator.to_f]
          hdr << data_out
        end
  }
}
knut
  • 27,320
  • 6
  • 84
  • 112
6

The cleanest way to do this is to open the file once, in mode 'w', write the headers, and then write the data.

If there's some technical reason that can't do this (e.g., the data isn't available all at once), then you can use the IO#tell method on the file to return the current file position. When you open the file for appending, the position is set to the end of the file, so if the current file position is zero, then the file was newly created and has no headers:

File.open('cdhu3_X.csv', 'a+') do |hdr|
  if hdr.tell() == 0  # file is empty, so write header
    hdr << "numerator, denominator, calculation\n"
  end
  hdr << "#{data_out}\n"
end
sfstewman
  • 5,589
  • 1
  • 19
  • 26
5

Best way to handle csv file is to use Ruby's CSV module.

I had same problem after reading CSV code I came across this solution which i find most efficient.

headers = ['col1','col2','col3']

CSV.open(file_path, 'a+', {force_quotes: true}) do |csv|
  csv << headers if csv.count.eql? 0 # csv.count method gives number of lines in file if zero insert headers
end
Asad Ali
  • 640
  • 6
  • 18
  • 2
    How would I change the original headers after inserting some data? Say I may have additional columns but only would know after the data has been populated? I've tried csv.headers.push but doesn't update the actual headers in the file it outputs. Thanks – scanales Apr 06 '16 at 19:56
  • Assuming you are only appending columns all the way to the right, you can open the file with a different snippet of code / rewind to overwrite the first line. – whodini9 Jul 27 '18 at 14:53
-1

This works for me

headers = ["Reference Number", "Vendor Line Code"]
CSV.open(file_path, "wb") do |csv|
    csv << headers
    @vendor.vendor_items.each do |vi|
      row_data  = [vi.reference_number, vi.line_code]
      csv << row_data
    end
end