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 User
s 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
Nokogiri: Select Content Between Element a and B
Problem Signing Out with Devise on My App
How to Post Xml to Restful Web Service Using Net::Http::Post
Overriding the == Operator in Ruby
Rails How to Update a Column After Saving
Consistent String#Hash Based Only on the String's Content
Running a Ruby Program as a Windows Service
Difference Between '%{}', '%Q{}', '%Q{}' in Ruby String Delimiters
Converting Utf8 to Ansi with Ruby
Rails 4 + Devise: Invalid Route Name, Already in Use
How to Get the Latest Record from Each Group in Activerecord
Visit_Psych_Nodes_Alias: Unknown Alias: Default (Psych::Badalias)
Git Deployment + Configuration Files + Heroku
Rails: Plus Sign in Get-Request Replaced by Space
Redis or Mongo for Determining If a Number Falls Within Ranges
Silencing Deprecation Warnings in Rails 3
Rails 3.1 with Postgresql: Group by Must Be Used in an Aggregate Function