/ Tags: RUBY-CODE / Categories: SOLUTIONS

Parse A Csv String Into An Array Of Hashes With Headers In Ruby

Uploaded spreadsheets and API exports arrive as CSV strings. Converting them into an array of hashes keyed by header name makes the rest of the code readable — row[:email] instead of row[3].

Description

Ruby’s csv library handles quoting, embedded commas, and newlines inside fields, which is why hand-splitting on commas breaks on real data. headers: true reads the first line as headers. header_converters: :symbol downcases them and turns them into symbols. map(&:to_h) converts each CSV::Row into a plain hash. Add converters: :numeric to coerce numbers automatically, and strip whitespace with a custom converter — exported CSVs are full of stray spaces.

Sample input:

  csv = "Name,Email,Age\nAda Lovelace,[email protected],36\n\"Smith, John\",[email protected],42\n"


Sample Output:

  # => [{ name: "Ada Lovelace", email: "[email protected]", age: 36 },
  #     { name: "Smith, John",  email: "[email protected]", age: 42 }]

Answer

  require "csv"

  def parse_csv(text)
    CSV.parse(
      text,
      headers: true,
      header_converters: :symbol,
      converters: [:numeric, ->(field) { field.is_a?(String) ? field.strip : field }]
    ).map(&:to_h)
  end

  parse_csv(csv)
  # => [{ name: "Ada Lovelace", email: "[email protected]", age: 36 }, ...]

  # Stream a large file instead of loading it into memory
  CSV.foreach("orders.csv", headers: true, header_converters: :symbol) do |row|
    Order.create!(row.to_h.slice(:sku, :quantity))
  end

  # Custom delimiter and encoding
  CSV.parse(text, headers: true, col_sep: ";", encoding: "bom|utf-8")

  # Reject rows where every value is blank
  parse_csv(csv).reject { |row| row.values.all? { |v| v.to_s.strip.empty? } }

Learn More

cdrrazan

Rajan Bhattarai

Full Stack Software Developer! 💻 🏡 Grad. Student, MCS. 🎓 Class of '23. GitKraken Ambassador 🇳🇵 2021/22. Works with Ruby / Rails. Photography when no coding. Also tweets a lot at TW / @cdrrazan!

Read More