Skip to content

Commit

Permalink
CSV.parse - Support Headers
Browse files Browse the repository at this point in the history
  • Loading branch information
Zeljko-Predjeskovic committed Dec 8, 2024
1 parent 6df6b3d commit ff5ed33
Show file tree
Hide file tree
Showing 3 changed files with 47 additions and 0 deletions.
11 changes: 11 additions & 0 deletions spec/std/csv/csv_parse_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,17 @@ describe CSV do
CSV.parse(%("","")).should eq([["", ""]])
end

it "parses to hashes" do
csv_text = "Index,Customer Id,First Name,Last Name
1,DD37Cf93aecA6Dc,Sheryl,Baxter
2,1Ef7b82A4CAAD10,Preston,Lozano
3,6F94879bDAfE5a6,,Berry"

CSV.parse_to_h(csv_text).should eq([{"Index" => "1", "Customer Id" => "DD37Cf93aecA6Dc", "First Name" => "Sheryl", "Last Name" => "Baxter"},
{"Index" => "2", "Customer Id" => "1Ef7b82A4CAAD10", "First Name" => "Preston", "Last Name" => "Lozano"},
{"Index" => "3", "Customer Id" => "6F94879bDAfE5a6", "First Name" => "", "Last Name" => "Berry"}])
end

it "raises if single quote in the middle" do
expect_raises CSV::MalformedCSVError, "Unexpected quote at line 1, column 4" do
CSV.parse(%(hel"lo))
Expand Down
18 changes: 18 additions & 0 deletions src/csv.cr
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,24 @@ class CSV
Parser.new(string_or_io, separator, quote_char).parse
end

# Parses a CSV or `IO` into an array of hashes using the first row as headers.
#
# Takes optional *separator* and *quote_char* arguments for defining
# non-standard csv cell separators and quote characters.
#
# ```
# require "csv"
#
# CSV.parse_to_hashes("name,age,city\nJohn,30,New York\nJane,25,San Francisco")
# # => [
# # {"name" => "John", "age" => "30", "city" => "New York"},
# # {"name" => "Jane", "age" => "25", "city" => "San Francisco"}
# # ]
# ```
def self.parse_to_h(string_or_io : String | IO, separator : Char = DEFAULT_SEPARATOR, quote_char : Char = DEFAULT_QUOTE_CHAR) : Array(Hash(String, String))
Parser.new(string_or_io, separator, quote_char).parse_to_h
end

# Yields each of a CSV's rows as an `Array(String)`.
#
# See `CSV.parse` about the *separator* and *quote_char* arguments.
Expand Down
18 changes: 18 additions & 0 deletions src/csv/parser.cr
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,16 @@ class CSV::Parser
rows
end

def parse_to_h : Array(Hash(String, String))
rows = [] of Hash(String, String)
if headers = next_row
while row = next_row
rows << parse_row_to_h_internal(headers, row)
end
end
rows
end

# Yields each of the remaining rows as an `Array(String)`.
def each_row(&) : Nil
while row = next_row
Expand Down Expand Up @@ -71,6 +81,14 @@ class CSV::Parser
end
end

private def parse_row_to_h_internal(headers : Array(String), row : Array(String)) : Hash(String, String)
h = {} of String => String
headers.each_with_index do |header, i|
h[header] = row[i].strip || ""
end
h
end

private struct RowIterator
include Iterator(Array(String))

Expand Down

0 comments on commit ff5ed33

Please sign in to comment.