How to Write Columns Header to a CSV File with Ruby

Issue writing data to csv file in ruby

While I cannot replicate your issues please try this instead:

headers = ["Full Name", "Email", "Phone Number"]
CSV.open("myfile.csv", "wb", write_headers: true, headers: headers ) do |csv|
User.where(id: inactive_users_id)
.pluck(:full_name, :email, :phone_number)
.each do |row|
csv << row
end
end

Here we are collecting all the Users in 1 query (rather than your current 3 per User) and just the needed columns are converted to an Array (using #pluck). Then we just push each one into the csv as a row

As pointed out in the comments in rails 3 pluck will not work with multiple columns (or at all depending on version) instead one should use select and then reference the attributes inside the loop to to create the row

Why does my CSV write a header after each row?

For the first one change the loop:

CSV.open(csvfile, "a+", write_headers: true, headers: ["Company_Name","Website","Street_Address", "City", "State", "Zip", "Phone","Email"]) do |csv|
info.each do |listing|
csv << listing
end
end

For the second:

header_written = {} # memorize if header is allready written
csvs.each do |csv|
...
# alternative can be 'write_headers: !File.exist?(new_csv)'
# but this works only when this file is created here and no empty file exists
CSV.open(new_csv, "a+", write_headers: !header_written[new_csv], headers: ["Company_Name","Website","Street_Address", "City", "State", "Zip", "Phone","Email"]) do |new_csv_row|
new_csv_row << row
puts "#{row['Email']} successfully added to #{new_csv}"
end
header_written[new_csv] ||= true

Edit: I did recognize just now that new_csv is not dynamic. So you can place the second CSV.open call also outside the loops or use a simple flag instead of header_written[] so you can change it to header_written = false|true .

Change header of specific column with Ruby CSV

I did not find this documented in examples, only in the source of csv.rb. Converters receive an optional second argument, field_info. According to the documentation, this is a Struct with the fields index, line, and header:

  • index: The zero-based index of the field in its row.
  • line: The line of the data source this row is from.
  • header: The header for the column, when available.

So as an example, this will convert the first header to "time" and leave others untouched:

CSV.new(f, header_converters: lambda{|h, field_info| field_info.index == 0 ? "time" : h })

Importing CSV's with different Column Headers each time in rails

Create a table for importing csv data to. Let's call it CsvDatum. Fix columns that are shared by all your csv's and create a third text column to store a hash containing the extra attributes. Let's say all csv's have name and email. You schema should be as follows.

  create_table "csv_data", force: :cascade do |t|
t.string "name", limit: 255
t.string "email", limit: 255
t.text "extra_columns", limit: 65535
end

In your model

class CsvDatum < ActiveRecord::Base
serialize :extra_columns, Hash
end

Now when you are reading your csv file create an instance of the CsvDatum class to hold row data.

CSV.foreach("path/to/file.csv", headers: true) do |row|
data = CsvDatum.new(name: row["name"], email: row["email"])
row.delete["name"]
row.delete["email"]
data.extra_columns = row.to_hash
data.save
end

Loading data from a CSV with header in Ruby

The signature for the CSV.open method is:

CSV.open(filename, mode = "rb", options = Hash.new)

Therefore calling it like this:

CSV.open(file, "r", { ... }, { ... }) 

...is incorrect, and Ruby should throw an exception when you do that.

The correct way to call CSV.open with a Hash is either:

CSV.open(file, "r", { :a => b, :c => d, :e => f })

Or:

CSV.open(file, "r", :a => b, :c => d, :e => f)

So, to fix your problem the solution should be to change this:

CSV.open(file, "r", { :col_sep => separator }, { :headers => headers })

To this:

CSV.open(file, "r", { :col_sep => separator, :headers => headers })

manipulating csv with ruby

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


Related Topics



Leave a reply



Submit