ruby / csv

CSV Reading and Writing
https://ruby.github.io/csv/
BSD 2-Clause "Simplified" License
177 stars 113 forks source link

row access method(like .first, .count, .map) remove row unintentionally #261

Closed tktk0430 closed 1 year ago

tktk0430 commented 1 year ago

https://paiza.io/projects/UZG89oyrPr2fhhw51QKH0A

require 'csv'

raw =<<~EOF
  name,age
  tom,13
  bob,14
  alice,15
EOF

csv = CSV.new(raw, headers: true)
puts csv.count #=> expect 3, and result is 3
puts csv.count #=> expect 3, but result is 0

csv = CSV.new(raw, headers: true)
puts csv.first['name'] #=> expect tom, and result is tom
puts csv.first['name'] #=> expect tom, but result is bob
puts csv.first['name'] #=> expect tom, but result is alice

In my sense, .count and .first is not destructive, but these method change csv instance data. What shoud I do to get result I expect.

Thank you.

kou commented 1 year ago

CSV object behaves like an IO. IO consumes data by reading. You can't read data again without seeking.

require "stringio"
lines = StringIO.new("a\nb\nc\n").each_line
p lines.first # => "a\n"
p lines.first # => "b\n"
p lines.first # => "c\n"

If you don't want to consume data with Enumerable methods such as #count and #first, you can use CSV#to_a or CSV#rewind:

require 'csv'

raw =<<~EOF
  name,age
  tom,13
  bob,14
  alice,15
EOF

csv = CSV.new(raw, headers: true)
rows = csv.to_a
puts rows.count #=> 3
puts rows.count #=> 3

csv = CSV.new(raw, headers: true)
rows = csv.to_a
puts rows.first['name'] #=> tom
puts rows.first['name'] #=> tom
puts rows.first['name'] #=> tom

csv = CSV.new(raw, headers: true)
puts csv.count #=> 3
csv.rewind
puts csv.count #=> 3

csv = CSV.new(raw, headers: true)
puts csv.first['name'] #=> tom
csv.rewind
puts csv.first['name'] #=> tom
csv.rewind
puts csv.first['name'] #=> tom
tktk0430 commented 1 year ago

I got it. Using to_a way seems better for my case. Thank you @kou !