Skip to content

Support block in CSV::Row#to_h#356

Merged
kou merged 13 commits intoruby:mainfrom
tsymbalenkovlad:main
May 3, 2026
Merged

Support block in CSV::Row#to_h#356
kou merged 13 commits intoruby:mainfrom
tsymbalenkovlad:main

Conversation

@tsymbalenkovlad
Copy link
Copy Markdown
Contributor

It will be consistent with Ruby's method, otherwise it has to be like row.entries.to_h { ... }

Comment thread lib/csv/row.rb Outdated
new_key, new_value = if block_given?
yield(key, value)
else
[key, value]
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't want to create a needless temporary Array.

Could you use

if block_given?
  each do |key, value|
    key, value = yield(key, value)
    # ...
  end
else
  each do |key, value|
    # ...
  end
end

or

each do |key, value|
  key, value = yield(key, value) if block_given?
  # ...
end

?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, that's a good one

Comment thread test/csv/test_row.rb Outdated
Comment on lines +342 to +344
row2 = CSV::Row.new(%w{A B C}, [1, 2, 3])
hash2 = row2.to_hash { |k, v| [k, v ** 2] }
assert_equal({"A" => 1, "B" => 4, "C" => 9}, hash2)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you create a separated test?

@tsymbalenkovlad tsymbalenkovlad requested a review from kou April 30, 2026 09:37
Comment thread lib/csv/row.rb Outdated
Comment on lines +655 to +656
each do |key, value|
key, value = yield(key, value) if block_given?
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, sorry. We need to call self[key]:

Suggested change
each do |key, value|
key, value = yield(key, value) if block_given?
each do |key, _value|
value = self[key]
key, value = yield(key, value) if block_given?

@tsymbalenkovlad tsymbalenkovlad requested a review from kou April 30, 2026 10:11
Comment thread test/csv/test_row.rb Outdated
Co-authored-by: Sutou Kouhei <kou@cozmixng.org>
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds support for passing a block to CSV::Row#to_h / #to_hash, aligning usage with Ruby’s to_h { ... } pattern and avoiding the need for row.entries.to_h { ... }.

Changes:

  • Add a new test covering CSV::Row#to_hash with a block.
  • Update CSV::Row#to_h to yield each key/value pair to an optional block before insertion.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 5 comments.

File Description
test/csv/test_row.rb Adds a test for block form of to_hash.
lib/csv/row.rb Implements yielding to an optional block in to_h during hash construction.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread lib/csv/row.rb Outdated
Comment on lines +655 to +659
each do |key, _value|
hash[key] = self[key] unless hash.key?(key)
value = self[key]
key, value = yield(key, value) if block_given?

hash[key] = value unless hash.key?(key)
Comment thread lib/csv/row.rb Outdated
Comment on lines +655 to +657
each do |key, _value|
hash[key] = self[key] unless hash.key?(key)
value = self[key]
key, value = yield(key, value) if block_given?
Comment thread lib/csv/row.rb Outdated
Comment on lines +653 to +659
def to_h
hash = {}
each do |key, _value|
hash[key] = self[key] unless hash.key?(key)
value = self[key]
key, value = yield(key, value) if block_given?

hash[key] = value unless hash.key?(key)
Comment thread test/csv/test_row.rb Outdated
Comment on lines +345 to +346
row = CSV::Row.new(%w{A B C}, [1, 2, 3])
hash = row.to_hash { |k, v| [k, v ** 2] }
Comment thread lib/csv/row.rb Outdated
Comment on lines +653 to +657
def to_h
hash = {}
each do |key, _value|
hash[key] = self[key] unless hash.key?(key)
value = self[key]
key, value = yield(key, value) if block_given?
@tsymbalenkovlad tsymbalenkovlad requested a review from kou April 30, 2026 12:01
Comment thread lib/csv/row.rb Outdated
hash = {}
each do |key, _value|
hash[key] = self[key] unless hash.key?(key)
next if hash.key?(key)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

key may be changed by yield(key, value). So we can't do this if block_given?.

How about using separated each?

if block_given?
  each do |key, _value|
    key, value = yield(key, self[key])
    hash[key] = value unless hash.key?(key)
  end
else
  each do |key, _value|
    hash[key] = self[key] unless hash.key?(key)
  end
end

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds block support to CSV::Row#to_h (and therefore #to_hash) to align with Ruby’s to_h { ... } conversion pattern, so callers don’t need to go through row.entries.to_h { ... }.

Changes:

  • Extend CSV::Row#to_h to accept an optional block that transforms each (key, value) pair before insertion.
  • Document the new block form in CSV::Row#to_h’s RDoc.
  • Add a unit test exercising to_hash with a block.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.

File Description
test/csv/test_row.rb Adds coverage for CSV::Row#to_hash accepting a block.
lib/csv/row.rb Implements and documents block support for CSV::Row#to_h/#to_hash.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread lib/csv/row.rb Outdated
Comment on lines +662 to +668
each do |key, _value|
hash[key] = self[key] unless hash.key?(key)
next if hash.key?(key)

value = self[key]
key, value = yield(key, value) if block_given?

hash[key] = value
Comment thread lib/csv/row.rb Outdated
next if hash.key?(key)

value = self[key]
key, value = yield(key, value) if block_given?
Comment thread test/csv/test_row.rb Outdated
Comment on lines +345 to +347
row = CSV::Row.new(%w{A A B C}, [1, 2, 2, 3])
hash = row.to_hash { |k, v| [k, v**2] }
assert_equal({"A" => 1, "B" => 4, "C" => 9}, hash)
@tsymbalenkovlad tsymbalenkovlad requested a review from kou May 1, 2026 07:36
@kou kou requested a review from Copilot May 1, 2026 12:04
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds support for passing a block to CSV::Row#to_h/#to_hash, aligning the API shape with Ruby’s to_h { ... } usage while keeping CSV::Row#to_h as the primary conversion entrypoint.

Changes:

  • Add a new test covering CSV::Row#to_hash with a block and basic error cases for invalid block return values.
  • Extend CSV::Row#to_h to accept an optional block and transform (key, value) pairs via the block result.
  • Update the to_h method documentation to include the block form and an example.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 4 comments.

File Description
test/csv/test_row.rb Adds coverage for to_hash when a block is provided, including error handling expectations.
lib/csv/row.rb Implements block support in CSV::Row#to_h and updates the method docs accordingly.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread lib/csv/row.rb Outdated
Comment on lines +666 to +669
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
Comment thread lib/csv/row.rb
Comment on lines +663 to +671
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
Comment thread test/csv/test_row.rb Outdated
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|
Comment thread lib/csv/row.rb Outdated
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
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated 5 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread lib/csv/row.rb Outdated
# 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}
Comment thread lib/csv/row.rb Outdated
Comment on lines +665 to +669
result = Array.try_convert(yield(key, self[key]))
raise TypeError, "wrong element type #{result.class} (expected array)" if result.nil?
raise ArgumentError, "wrong array length (expected 2, was #{result.size})" unless result.size == 2

key, value = result
Comment thread lib/csv/row.rb Outdated

if block_given?
each do |key, _value|
result = Array.try_convert(yield(key, self[key]))
Comment thread lib/csv/row.rb
Comment on lines +674 to +676
each do |key, _value|
hash[key] = self[key] unless hash.key?(key)
end
Comment thread test/csv/test_row.rb Outdated
Comment on lines +344 to +348
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)
Comment thread lib/csv/row.rb Outdated
Comment on lines +670 to +671
key.freeze if key.is_a?(String) && !key.frozen?
hash[key] = value unless hash.key?(key)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you avoid needless freeze check?

Suggested change
key.freeze if key.is_a?(String) && !key.frozen?
hash[key] = value unless hash.key?(key)
next if hash.key?(key)
key.freeze if key.is_a?(String) && !key.frozen?
hash[key] = value

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@tsymbalenkovlad What do you think about this?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh sorry I didn't see code suggestion and thought it's about removing !key.frozen?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks.

@tsymbalenkovlad tsymbalenkovlad requested a review from kou May 2, 2026 07:02
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread lib/csv/row.rb
Comment on lines +663 to +673
if block_given?
each do |key, _value|
result = yield(key, self[key])
result_array = Array.try_convert(result)
raise TypeError, "wrong element type #{result.class} (expected array)" if result_array.nil?
raise ArgumentError, "wrong array length (expected 2, was #{result_array.size})" unless result_array.size == 2

key, value = result_array
key.freeze if key.is_a?(String)
hash[key] = value unless hash.key?(key)
end
Comment thread test/csv/test_row.rb Outdated
Comment on lines +344 to +360
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.each_key do |string_key|
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
Comment thread test/csv/test_row.rb Outdated
Comment on lines 352 to 357
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.each_key do |string_key|
hash2 = row.to_hash { |k, v| [new_keys_map[k], v**2] }
assert_equal({"A" => 1, "B" => 4}, hash2)
hash2.each_key do |string_key|
assert_predicate(string_key, :frozen?)
end
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you split this duplicated key case to a separated test?

We can merge this after it.

Comment thread lib/csv/row.rb Outdated
Comment on lines +670 to +671
key.freeze if key.is_a?(String) && !key.frozen?
hash[key] = value unless hash.key?(key)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks.

@tsymbalenkovlad tsymbalenkovlad requested a review from kou May 3, 2026 06:47
@kou kou merged commit 4a5c504 into ruby:main May 3, 2026
60 of 68 checks passed
@kou
Copy link
Copy Markdown
Member

kou commented May 3, 2026

Thanks.

Do you want to use this feature soon? (Should we release a new version soon?)

@tsymbalenkovlad
Copy link
Copy Markdown
Contributor Author

@kou No rush, I have a patch in project

Thanks

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants