diff --git a/lib/csv/row.rb b/lib/csv/row.rb index 86323f7..f711bf1 100644 --- a/lib/csv/row.rb +++ b/lib/csv/row.rb @@ -637,6 +637,7 @@ def ==(other) # :call-seq: # row.to_h -> hash + # row.to_h {|key, value| ... } -> hash # # Returns the new \Hash formed by adding each header-value pair in +self+ # as a key-value pair in the \Hash. @@ -650,11 +651,30 @@ def ==(other) # table = CSV.parse(source, headers: true) # row = table[0] # row.to_h # => {"Name"=>"Foo"} + # + # If a block is given, will call it with (key, value) arguments and use result as a hash entry: + # source = "Name,Value\nfoo,1\nbar,2\nbaz,3\n" + # table = CSV.parse(source, headers: true) + # row = table[0] + # row.to_h { |key, value| [key, value.to_i * 2] } # => {"Name"=>"foo", "Value"=>2} def to_h hash = {} - each do |key, _value| - hash[key] = self[key] unless hash.key?(key) + + if block_given? + each do |key, _value| + result = yield(key, self[key]) + raise TypeError, "wrong element type #{result.class} (expected array)" unless result.is_a?(Array) + raise ArgumentError, "wrong array length (expected 2, was #{result.size})" unless result.size == 2 + + key, value = result + hash[key] = value unless hash.key?(key) + end + else + each do |key, _value| + hash[key] = self[key] unless hash.key?(key) + end end + hash end alias_method :to_hash, :to_h diff --git a/test/csv/test_row.rb b/test/csv/test_row.rb index b717945..260b1dd 100644 --- a/test/csv/test_row.rb +++ b/test/csv/test_row.rb @@ -341,6 +341,24 @@ def test_to_hash end end + def test_to_hash_with_block + row = CSV::Row.new(%w{A A B C}, [1, 2, 2, 3]) + new_keys_map = {"A" => "A", "B" => "B", "C" => "B"} + hash = row.to_hash { |k, v| [new_keys_map[k], v**2] } + assert_equal({"A" => 1, "B" => 4}, hash) + hash.keys.each_with_index do |string_key, h| + assert_predicate(string_key, :frozen?) + end + + assert_raise TypeError do + row.to_hash { "foo" } + end + + assert_raise ArgumentError do + row.to_hash { [1] } + end + end + def test_to_csv # normal conversion assert_equal("1,2,3,4,\n", @row.to_csv)