I am new in ruby so please forgive the noobishness.
I have a CSV with two columns. One for animal name and one for animal type. I have a hash with all the keys being animal names and the values being animal type. I would like to write the hash to the CSV without using fasterCSV. I have thought of several ideas what would be easiest.. here is the basic layout.
require "csv"
def write_file
  h = { 'dog' => 'canine', 'cat' => 'feline', 'donkey' => 'asinine' }
  CSV.open("data.csv", "wb") do |csv|
    csv << [???????????]
  end
end
When I opened the file to read from it I opened it File.open("blabla.csv", headers: true)
Would it be possible to write back to the file the same way? 
If you want column headers and you have multiple hashes:
require 'csv'
hashes = [{'a' => 'aaaa', 'b' => 'bbbb'}]
column_names = hashes.first.keys
s=CSV.generate do |csv|
  csv << column_names
  hashes.each do |x|
    csv << x.values
  end
end
File.write('the_file.csv', s)
(tested on Ruby 1.9.3-p429)
Try this:
require 'csv'
h = { 'dog' => 'canine', 'cat' => 'feline', 'donkey' => 'asinine' }
CSV.open("data.csv", "wb") {|csv| h.to_a.each {|elem| csv << elem} }
Will result:
1.9.2-p290:~$ cat data.csv 
dog,canine
cat,feline
donkey,asinine
                        I think the simplest solution to your original question:
def write_file
  h = { 'dog' => 'canine', 'cat' => 'feline', 'donkey' => 'asinine' }
  CSV.open("data.csv", "w", headers: h.keys) do |csv|
    csv << h.values
  end
end
With multiple hashes that all share the same keys:
def write_file
  hashes = [ { 'dog' => 'canine', 'cat' => 'feline', 'donkey' => 'asinine' },
             { 'dog' => 'rover', 'cat' => 'kitty', 'donkey' => 'ass' } ]
  CSV.open("data.csv", "w", headers: hashes.first.keys) do |csv|
    hashes.each do |h|
      csv << h.values
    end
  end
end
                        CSV can take a hash in any order, exclude elements, and omit a params not in the HEADERS
require "csv"
HEADERS = [
  'dog',
  'cat',
  'donkey'
]
def write_file
  CSV.open("data.csv", "wb", :headers => HEADERS, :write_headers => true) do |csv|
    csv << { 'dog' => 'canine', 'cat' => 'feline', 'donkey' => 'asinine' }
    csv << { 'dog' => 'canine'}
    csv << { 'cat' => 'feline', 'dog' => 'canine', 'donkey' => 'asinine' }
    csv << { 'dog' => 'canine', 'cat' => 'feline', 'donkey' => 'asinine', 'header not provided in the options to #open' => 'not included in output' }
  end
end
write_file # => 
# dog,cat,donkey
# canine,feline,asinine
# canine,,
# canine,feline,asinine
# canine,feline,asinine
This makes working with the CSV class more flexible and readable.
I tried the solutions here but got an incorrect result (values in wrong columns) since my source is a LDIF file that not always has all the values for a key. I ended up using the following.
First, when building up the hash I remember the keys in a separate array which I extend with the keys that are not allready there.
# building up the array of hashes
File.read(ARGV[0]).each_line do |lijn|
    case
    when lijn[0..2] == "dn:" # new record
        record = {}
    when lijn.chomp == '' # end record
        if record['telephonenumber'] # valid record ?
            hashes << record
            keys = keys.concat(record.keys).uniq
        end
    when ...
    end
end
The important line here is keys = keys.concat(record.keys).uniq which extends the array of keys when new keys (headers) are found.
Now the most important: converting our hashes to a CSV
CSV.open("export.csv", "w", {headers: keys, col_sep: ";"}) do |row|
  row << keys # add the headers
  hashes.each do |hash|
    row << hash # the whole hash, not just the array of values
  end
end
                        [BEWARE] All the answers in this thread are assuming that the order of the keys defined in the hash will be constant amongst all rows.
To prevent problems (that I am facing right now) where some values are assigned to the wrong keys in the csv (Ex:)
hahes = [
    {:cola => "hello", :colb => "bye"},
    {:colb => "bye", :cola => "hello"}
]
producing the following table using the code from the majority (including best answer) of the answers on this thread:
cola  | colb
-------------
hello | bye
-------------
bye   | hello
You should do this instead:
require "csv"
csv_rows = [
    {:cola => "hello", :colb => "bye"},
    {:colb => "bye", :cola => "hello"}
]
column_names = csv_rows.first.keys
s=CSV.generate do |csv|
  csv << column_names
  csv_rows.each do |row|
    csv << column_names.map{|column_name| row[column_name]} #To be explicit
  end
end
                        If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With