Bug #10013

[CSV] Yielding all elements from a row

Added by Gat (Dawid Janczak) almost 3 years ago. Updated almost 3 years ago.

Target version:
ruby -v:
ruby 2.2.0dev (2014-07-06 trunk 46722) [x86_64-linux]


Let's say I have the following CSV file:

I want to iterate over values yielding them to a block. I can do that like this:
CSV.foreach('file.csv') { |col1, col2, col3| print col2 + " " } # => "col2 2 5"
This works fine, but I would like to skip the headers:
CSV.foreach('file.csv', headers: true) { |col1, col2, col3| print col2 + " " } # => NoMethodError

CSV yields rows as arrays if headers option is not specified and destructuring works fine.
When headers option is specified however, CSV::Row objects are yielded instead and destructuring fails.

It would be nice to have both scenarios working in the same manner, but I don't know how to approach this. Calling to_a on yielded row ( worked, but obviously this would break when people actually expect CSV::Row instance. Any ideas?


#1 [ruby-core:63583] Updated by Gat (Dawid Janczak) almost 3 years ago

Sorry, this should be in lib category, but I'm not able to change it now.

#2 [ruby-core:63585] Updated by avit (Andrew Vit) almost 3 years ago

Do you mean that it should consider the block arity to decide whether to yield a Row or destructure it into column parts? i.e.

CSV.foreach('file.csv', headers: true) do |col1, col2, col3| 
  col1 == "1"
  col2 == "2"
  col3 == "3"

and also

CSV.foreach('file.csv', headers: true) do |row| 
  row["col1"] == "1"
  row["col2"] == "2"
  row["col3"] == "3"

I think this would be too confusing and magical. What to do when the block arity doesn't match the count of row items? What about the case of a CSV with one column?

I haven't tried, but this might do what you expect (if you must use headers in the input):

CSV.foreach('file.csv', headers: true) do |col1, col2, col3| 
  col1 == "1"
  col2 == "2"
  col3 == "3"

I'll leave it for someone else to chime in whether there's a case for a special method here (e.g. "foreach_array").

#3 [ruby-core:63904] Updated by hsbt (Hiroshi SHIBATA) almost 3 years ago

  • Assignee set to JEG2 (James Gray)
  • Category set to lib
  • Target version set to 2.2.0
  • Status changed from Open to Assigned

#4 [ruby-core:64099] Updated by Gat (Dawid Janczak) almost 3 years ago

First of all sorry for the late answer Andrew.

Checking arity was one thing I was considering. You're right that the arity of the block might not match the number of items, but that works fine with arrays.

foo = [[1, 2], [3, 4]]
foo.each { |el| p el } # => prints [1, 2] then [3, 4]
foo.each { |el1, el2| p el2 } # => prints 2 then 4
foo.each { |el1, el2, el3| p el3 } # => prints nil then nil

This is basically the same behaviour as parallel assignment with more LVals than RVals.

Another thing I was considering was to always yield CSV::Row objects.

Also available in: Atom PDF