Reputation: 23
i'm building a CSV file using ruby and i have different columns, i.e.
"company_name", "company_phones"
The company name is no issue since it's a simple string, but the company has more than one phone, which in my code is being stored as
phones = [phone1, phone2]
When i add it into the CSV as follows:
CSV.open("output.csv", "a") do |csv|
csv << [
company_name,
company_phones,
]
end
I've tried different ways to do it but it ends up:
1) Showing up with double double quotes ([""Phone1"", ""Phone2""]) 2) And this, when reading the CSV, shows like ["\"Phone1"", "\"Phone2""] and it's not even an array but a string.
Is there something i'm missing, or it just can't be done writing CSV and i should change the format of my output?
Upvotes: 0
Views: 9983
Reputation: 110685
This situation calls for a CSV custom converter.
Write the CSV file
Let's first create a CSV file from the following information.
FName = 't.csv'
headers = ["company", "vals", "phone_nbrs"]
rows = [
['ABC', [1.3, 2.1, 4.1], ["800-555-1000", "800-555-1001"]],
['XYZ', [7.3, 9.5], ["800-555-2000", "800-555-2001"]]
]
I will do that by converting each of the arrays in rows
to a string containing elements of the array converted to strings and separated by a space. For example, [1.3, 2.1, 4.1]
will be saved as the string:
[1.3, 2.1, 4.1].join(' ')
#=> "1.3 2.1 4.1"
As will be seen, this particular string representation of arrays is arbitrary, just one among many ways of doing that. For example we could save the array of floats shown above as "1.3:2.1:4.1"
or "1.3¯\\_(ツ)_/¯2.1¯\\_(ツ)_/¯4.1"
.
Let's now create the CSV file.
require 'csv'
CSV.open(FName, "w") do |csv|
csv << headers
rows.each { |name,vals,phone_nbrs|
csv << [name, vals.join(' '), phone_nbrs.join(' ')] }
end
Let's see what was written.
puts File.read(FName)
company,vals,phone_nbrs
ABC,1.3 2.1 4.1,800-555-1000 800-555-1001
XYZ,7.3 9.5,800-555-2000 800-555-2001
Read the CSV file
The trick here is to define a converter that converts the strings of strings representing floats to arrays of floats and the strings of telephone numbers to arrays of strings.
convert_arrays = lambda do |value, field|
case field.header
when 'vals'
value.split.map(&:to_f)
when 'phone_nbrs'
value.split
else
value
end
end
#=> #<Proc:0x0000599427913ec0@(irb):19 (lambda)>
Now read the file into an array.
CSV.foreach(FName, :headers => true, converters: [convert_arrays]).
with_object([]) { |row, arr|
arr << row.values_at('company', 'vals', 'phone_nbrs') }
#=> [["ABC", [1.3, 2.1, 4.1], ["800-555-1000", "800-555-1001"]],
# ["XYZ", [7.3, 9.5], ["800-555-2000", "800-555-2001"]]]
See CSV::new and these examples of CSV converters.
Upvotes: 5