Emre
Emre

Reputation: 81

Ruby - Array of Hashes to CSV

I have collected some data and saved it to an array of hashes in the form of:

info = [
  {'Name' => 'Alex', 'Tel' => 999, 'E-mail' => "[email protected]"},
  {'Name' => 'Ali', 'Tel' => 995, 'E-mail' => "[email protected]"}
  # ...
]

But not all information is always there:

{'Name' => 'GuyWithNoTelephone', 'E-mail' => "[email protected]"}

I want to turn this information into a CSV file. Here is what I tried:

def to_csv (info)
  CSV.open("sm-data.csv", "wb") do |csv|
  csv << ["Name", "Tel", "E-mail"]
    info.each do |person|
      csv << person.values

When I try this, the format of the table is not correct, and say, if a telephone number is missing, then the e-mail of that person appears at telephone column.

How can I selectively write this info to the CSV file, i.e how can I tell to skip columns if a value is missing?

Upvotes: 2

Views: 2546

Answers (3)

Caner
Caner

Reputation: 1498

Simple as this:

path = "data/backtest-results.csv"
CSV.open(path, "wb") do |csv|
  csv << ["Asset", "Strategy", "Profit"]
  result_array.each do |p|
    csv << p.map { |key, value| value }
  end
end

use "(File.file?(path) ? "ab" : "wb")" rather than "wb" if you want to continue adding as the new data comes

Upvotes: 0

Vamsi Krishna
Vamsi Krishna

Reputation: 3792

Try this,

  def to_csv(csv_filename="sm-data.csv")
    # Get all unique keys into an array:
    keys = info.map(&:keys).inject(&:|)
    CSV.open(csv_filename, "wb") do |csv|
      csv << keys
      info.each do |hash|
        # fetch values at keys location, inserting null if not found.
        csv << hash.values_at(*keys)
      end
    end
  end

Upvotes: 4

Stefan
Stefan

Reputation: 114178

But sadly when I try this, the format of the table is not correct, and say, if a telephone number is missing, then the e-mail of that person appears at telephone column.

That's because you are omitting the telephone number in that case, providing just 2 of the 3 values. To fix this, you simply have to provide all 3 values, even if they don't exist in the hash:

csv << ['Name', 'Tel', 'E-mail']
info.each do |person|
  csv << [person['Name'], person['Tel'], person['E-Mail']]
end

or via Hash#values_at:

csv << ['Name', 'Tel', 'E-mail']
info.each do |person|
  csv << person.values_at('Name', 'Tel', 'E-Mail')
end

For:

{'Name' => 'GuyWithNoTelephone', 'E-mail' => "[email protected]"}

this results in:

csv << ['GuyWithNoTelephone', nil, '[email protected]']

which generates this output: (note the two commas, denoting an empty field in-between)

"GuyWithNoTelephone,,[email protected]\n"

Upvotes: 9

Related Questions