diff --git a/doc/csv/recipes/parsing.rdoc b/doc/csv/recipes/parsing.rdoc index 63673072..f9571161 100644 --- a/doc/csv/recipes/parsing.rdoc +++ b/doc/csv/recipes/parsing.rdoc @@ -61,6 +61,8 @@ All code snippets on this page assume that the following has been executed: - {Using Multiple Header Converters}[#label-Using+Multiple+Header+Converters] - {Recipe: Specify Multiple Header Converters in Option :header_converters}[#label-Recipe-3A+Specify+Multiple+Header+Converters+in+Option+-3Aheader_converters] - {Recipe: Specify Multiple Header Converters in a Custom Header Converter List}[#label-Recipe-3A+Specify+Multiple+Header+Converters+in+a+Custom+Header+Converter+List] +- {Pattern Matching}[#label-Pattern+Matching] + - {Recipe: Match Parsed Rows by Header or Position}[#label-Recipe-3A+Match+Parsed+Rows+by+Header+or+Position] - {Diagnostics}[#label-Diagnostics] - {Recipe: Capture Unconverted Fields}[#label-Recipe-3A+Capture+Unconverted+Fields] - {Recipe: Capture Field Info}[#label-Recipe-3A+Capture+Field+Info] @@ -522,6 +524,45 @@ Apply multiple header converters by defining and registering a custom header con parsed = CSV.parse(source, headers: true, header_converters: :my_header_converters) parsed.headers # => [:name, :value] +=== Pattern Matching + +==== Recipe: Match Parsed Rows by Header or Position + +This example requires Ruby 3.0 or later. +Hash patterns use symbol keys. The built-in +:symbol+ header converter +also downcases headers and replaces spaces with underscores: + + source = <<~CSV + First Name,Department,Salary + Bob,Engineering,1000 + Jane,Sales,2000 + John,Management,5000 + CSV + data = CSV.parse(source, headers: true, header_converters: :symbol) + data.headers # => [:first_name, :department, :salary] + selected = data.select { _1 in first_name: /^J/ } + selected.map(&:fields) + # => [["Jane", "Sales", "2000"], ["John", "Management", "5000"]] + +Headers are strings by default. So a symbol key doesn't match: + + data = CSV.parse(source, headers: true) + data.headers # => ["First Name", "Department", "Salary"] + data.select { _1 in first_name: /^J/ } # => [] + +To keep the original case, use a custom converter that calls +to_sym+: + + case_sensitive = CSV.parse("Name\nJane\n", headers: true, + header_converters: ->(header) { header.to_sym }) + case_sensitive.headers # => [:Name] + case_sensitive.first in Name: /^J/ # => true + +An array pattern matches the row's fields independently of header key types: + + selected = data.select { _1 in [/^J/, _, _] } + selected.map(&:fields) + # => [["Jane", "Sales", "2000"], ["John", "Management", "5000"]] + === Diagnostics ==== Recipe: Capture Unconverted Fields diff --git a/lib/csv/row.rb b/lib/csv/row.rb index 40988001..414f59f5 100644 --- a/lib/csv/row.rb +++ b/lib/csv/row.rb @@ -688,6 +688,10 @@ def to_h # # Returns the new \Hash suitable for pattern matching containing only the # keys specified as an argument. + # Hash pattern keys are symbols. The +:symbol+ header converter downcases + # headers and replaces spaces with underscores: "First Name" + # becomes +:first_name+. To keep the original case, use + # header_converters: ->(header) { header.to_sym } instead. def deconstruct_keys(keys) if keys.nil? to_h