pjw23
pjw23

Reputation: 73

Creating a CSV from a hash

I need help understanding how to dump my hash contents into a csv. I found the following post that is exactly what I'm trying to do, but unfortunately there is no example on how to call the method so that it will write to a file.

original post Smartly converting array of hashes to CSV in ruby

       def to_csv(csv_filename="hash.csv")
         require 'csv'
         # Get all unique keys into an array:
         keys = self.flat_map(&:keys).uniq
         CSV.open(csv_filename, "wb") do |csv|
           csv << keys
           self.each do |hash|
             # fetch values at keys location, inserting null if not found.
             csv << hash.values_at(*keys)
           end
       end   
end

My array of hashes is called results and looks something like this: (Note: some keys have nil values)

[{:name =>"Mike", :job = "Teacher", :status = "single"},
{:name=> "Joe", :job =>nil, :status = "married"},
{:name =>"Sally", :job = "Nurse", :status = "single"}]

I would like to dump the entire contents of the results hash into a CSV file for further manipulation. My problem is I can't figure out how to get the results into csv file. When I run:

results.to_csv

nothing happens, no file is created. I thought I may have to first seed a blank file, but even then, no contents go into the file.

Can someone please help me understand what I'm doing wrong and why this isn't working. Thanks in advance for any assistance.

Upvotes: 1

Views: 3343

Answers (2)

Cary Swoveland
Cary Swoveland

Reputation: 110665

You need to define this method as an instance method of the class Array.

class Array
  def to_csv(csv_filename="hash.csv")
    require 'csv'
    # Get all unique keys into an array:
    keys = self.flat_map(&:keys).uniq
    CSV.open(csv_filename, "wb") do |csv|
      csv << keys
      self.each do |hash|
        # fetch values at keys location, inserting null if not found.
        csv << hash.values_at(*keys)
      end
    end   
  end
end

[{ :a=>1, :b=>2 }, { :c=>3 }].to_csv
  #=> [{:a=>1, :b=>2}, {:c=>3}] 

Let's see what was written.

CSV.read("hash.csv")
  #=> [["a", "b", "c"], ["1", "2", nil], [nil, nil, "3"]] 

I infer this is an array method from the code fragment

self.flat_map(&:keys)

As the mapping is to keys, it suggests that the reiceiver is an array of hashes.(That expression could have been written flat_map(&:keys).)

This method does not appear to meet your requirements.

You could save the keys and values thusly:

CSV.open("hash.csv", "wb") do |csv|
  arr.each do |h|
    csv << h
  end
end
  #=> [["a", "b", "c"], [1, 2, 3], [4, 5, 6]]

CSV.read("hash.csv")
  #=> [["a", "b", "c"], [1, 2, 3], [4, 5, 6]]

Upvotes: 4

Andr&#233;s
Andr&#233;s

Reputation: 624

Try:

require 'csv'

  def to_csv(csv_filename="hash.csv")
    a = [{:name =>"Mike", :job => "Teacher", :status => "single"},
    {:name=> "Joe", :job =>nil, :status => "married"},
    {:name =>"Sally", :job => "Nurse", :status => "single"}]
    values = a.map do |h|
      h.sort.to_h.values
    end

    CSV.open(csv_filename, "wb") do |csv|
      values.each do |row|
        csv << row
      end
    end
  end

EDIT

Note: This works for your case, where all your columns are in the array of hashs. For example you jave "Joe" with :job with value nil. if this is not your case, using value_at is the best chose

Upvotes: 0

Related Questions