I am not able to understand how CSV#convert work?

require ‘csv’

str = <<_
name,age,eligible?
arup,27
deep,14
Debu,26,y
Ram,30
Sagar,14
_
csv = CSV.new(str,:headers => true)
CSV::Converters[:eligibe] = lambda { |name| name = “Y” unless
name.empty? }
csv.convert(‘eligible?’,&CSV::Converters[:eligibe]) # => [nil]
csv.each { |row| p row }

~> /home/kirti/.rvm/rubies/ruby-2.0.0-p0/lib/ruby/2.0.0/csv.rb:2167:in

block (2 levels) in convert_fields': undefined methodarity’ for
nil:NilClass (NoMethodError)

Could any one help me to fix this? But I would expect a fix on this
method only, as I am trying to learn about this method. Basically how
does it work?

Thanks,
Arup

On Feb 16, 2014, at 10:50 PM, Arup R. [email protected] wrote:

Could any one help me to fix this? But I would expect a fix on this
method only, as I am trying to learn about this method. Basically how
does it work?

The docs are accurate, even if not very clear. The method can be called
with a name or a block, but not both.

If a name is given then it should be one of the built-in converters
(like :numeric, :date, etc).

With a block you can accept one or two arguments. To convert based on
the header name, or index, you want to specify a block with two
arguments to get the field info:

csv.convert {|field, info|
case info[:header]
when 'eligible?
# return converted eligible? value
when another
# return converted field value
else
field
end
}

Regards,
Ammar

Ammar A. wrote in post #1136869:

On Feb 16, 2014, at 10:50 PM, Arup R. [email protected] wrote:

If a name is given then it should be one of the built-in converters
(like :numeric, :date, etc).

With a block you can accept one or two arguments. To convert based on
the header name, or index, you want to specify a block with two
arguments to get the field info:

Thanks for the idea. But my code gives all nil, except for ‘y’ for the
field eligible?, for all rows.

require ‘csv’

str = <<_
name,age,eligible?
arup,27
deep,14
Debu,26
Ram,30
Sagar,14
_
csv = CSV.new(str,:headers => true,:col_sep => “,”)

csv.convert do |field, info|
#p info
case info[:header]
when ‘eligible?’
“Y”
else
field
end
end

csv.each { |row| p row.fields }

>> [“arup”, “27”, nil]

>> [“deep”, “14”, nil]

>> [“Debu”, “26”, nil]

>> [“Ram”, “30”, nil]

>> [“Sagar”, “14”, nil]

On Feb 17, 2014, at 12:46 AM, Arup R. [email protected] wrote:

Ram,30
Sagar,14
_

There are only two columns of data in you sample. If there was at least
a final separator, the converter would be called with a nil value, which
you can interpret as a false, if thats what you want.

require ‘csv’

str = <<_
name,age,eligible?
arup,27,
deep,14
Debu,26,y
Ram,30
Sagar,14
_

csv = CSV.new(str,:headers => true)

csv.convert {|field, info|
puts “CALLED”

case info[:header]
when ‘eligible?’
field.nil? ? false : (field.downcase[0] == ‘y’ ? true : false)
else
field
end
}

csv.each { |row| p row.fields }

Good luck,
Ammar

Ammar A. wrote in post #1136880:

On Feb 17, 2014, at 12:46 AM, Arup R. [email protected] wrote:

There are only two columns of data in you sample. If there was at least
a final separator, the converter would be called with a nil value, which
you can interpret as a false, if that’s what you want.

Got your point and the below output is also telling the same. In the
output I can see #<struct CSV::FieldInfo index=2, line=2, header="eligible?"> only for the first row and third row. This is
because there is a comma separator(,) exist. It means I can apply the
CSV#convert method only when there is a data, otherwise not.

require ‘csv’

str = <<_
name,age,eligible?
arup,27,
deep,14
Debu,26,y
Ram,30
Sagar,14
_

csv = CSV.new(str,:headers => true,:return_headers => true)

csv.convert { |field, info|
puts info

case info[:header]
when ‘eligible?’
field.nil? ? false : (field.downcase[0] == ‘y’ ? true : false)
when ‘name’,‘age’
field
else
“y”
end
}

csv.each { |row| row.fields }

output :

#
#
#
#
#
#
#
#
#
#
#
#

So my actual requirement is something else. But before that I need to
fix this part. As I actually have the data as below :

=====================
name,age,eligible?
arup,27
deep,14
Debu,26,y
Ram,30
Sagar,14

So, on parse time, can I insert the third column value, if it is not
present. It means I am looking for the below :

=====================
name,age,eligible?
arup,27,
deep,14,
Debu,26,y
Ram,30,
Sagar,14,

Can any one tell me what wrong I am doing here ?

============================================
require ‘csv’

str = <<_
name,age,eligible?
arup,27,
deep,14
Debu,26,y
Ram,30
Sagar,14
_

csv = CSV.new(str,:headers => true,:return_headers => true)

CSV.open(‘test.csv’,‘w’) do |csv_file|
csv.each do |row|
csv_file << row
end
csv_file.rewind
csv_file.convert do |field, info|
case info[:header]
when ‘eligible?’
“Y”
else
field
end
end
end

CSV.foreach(‘test.csv’) do |row|
p row
end

==========================================

In the output, still no “Y” value present.

>> [“name”, “age”, “eligible?”]

>> [“arup”, “27”, nil]

>> [“deep”, “14”, nil]

>> [“Debu”, “26”, “y”]

>> [“Ram”, “30”, nil]

>> [“Sagar”, “14”, nil]


Also I found that, csv_file.convert do |field, info|.. line of codes
are not getting executed. Why so ?

On Feb 17, 2014, at 3:36 PM, Arup R. [email protected] wrote:

Ram,30
csv_file.convert do |field, info|
p row

>> [“Ram”, “30”, nil]

>> [“Sagar”, “14”, nil]


Also I found that, csv_file.convert do |field, info|.. line of codes
are not getting executed. Why so ?

As you haven’t said what you expected it to output this is a guess:

I think that you need to put the converter on the csv object you use to
read from the string, so that as each record is read it will be
processed by the converter.

For example:

#!/usr/bin/env ruby
require ‘csv’

str = <<_
name,age,eligible?
arup,27,
deep,14
Debu,26,y
Ram,30
Sagar,14
_

csv = CSV.new(str,
:headers => true,
:return_headers => true
)

csv.convert do |field, info|
if info.header == ‘eligible?’
‘Y’
else
field
end
end

CSV.open(‘test.csv’,‘w’) do |csv_file|
csv.each do |row|
csv_file << row
end
end

CSV.foreach(‘test.csv’) do |row|
p row
end

produces:

~ ∙ ruby ~/tmp/try.rb
[“name”, “age”, “eligible?”]
[“arup”, “27”, “Y”]
[“deep”, “14”, nil]
[“Debu”, “26”, “Y”]
[“Ram”, “30”, nil]
[“Sagar”, “14”, nil]

Both Debu and Arup records had a eligible? field, and when the converter
is run for it the value is replaced with a “Y”.

Hope this helps,

Mike

Mike S. [email protected]
http://www.stok.ca/~mike/

The “`Stok’ disclaimers” apply.

On Feb 18, 2014, at 8:26 AM, Arup R. [email protected] wrote:

Why so ?

Because convert is called during a read not a write. Even if it did, you
add the row to csv_file before adding the converter. Dont bother
switching them, it will not be called on write.

It is not clear what you are trying to do. Why read then write then read
again? Are you trying to fix bad data? Can that be done at the source
instead?

Anyway, you can fix the value while reading, from a string or file,
without using convert. For example:

CSV.new(str).each do |row|

check/change field if needed

row[2] = ‘y’ unless row[2]

p row
end

Regards,
Ammar

require ‘csv’

str = <<_
name,age,eligible?
arup,27
deep,14
Debu,26,y
Ram,30
Sagar,14
_

csv = CSV.new(str,:headers => true,:return_headers => true)
csv.each do |row|
row[‘eligible?’] = “y” if row[‘eligible?’].nil?
end

csv.rewind
csv.each { |row| p row.fields }

output

[“name”, “age”, “eligible?”]
[“arup”, “27”, nil]
[“deep”, “14”, nil]
[“Debu”, “26”, “y”]
[“Ram”, “30”, nil]
[“Sagar”, “14”, nil]

===============
Again nil … I am getting…very frustrating :frowning:

On Feb 18, 2014, at 10:39 AM, Arup R. [email protected] wrote:

Again nil … I am getting…very frustrating :frowning:

No, it is not nil. Try this:

csv = CSV.new(str,:headers => true,:return_headers => true)
csv.each do |row|
row[‘eligible?’] = “y” if row[‘eligible?’].nil?
p row.fields
end

If you want the changes to persist you need to save them somehow. If you
dont save the changes and call rewind, you need to make the changes
again.

Ruby is very advanced, but it does not read minds :wink:

Good luck,
Ammar

Mike S. wrote in post #1136966:

On Feb 17, 2014, at 3:36 PM, Arup R. [email protected] wrote:

As you haven’t said what you expected it to output this is a guess:

I think that you need to put the converter on the csv object you use to
read from the string, so that as each record is read it will be
processed by the converter.

Ok… Let me try to make it more clear. This is the one, giving me more
trouble. Hope now it would be clear to you guys.

code

require ‘csv’

str = <<_
name,age,eligible?
arup,27
deep,14
Debu,26,y
Ram,30
Sagar,14
_

csv = CSV.new(str,:headers => true,:return_headers => true)

CSV.open(‘test.csv’,‘w’) do |csv_file|
csv.each do |row|
row[‘eligible?’] = " " if row[‘eligible?’].nil?
csv_file << row
csv_file.convert do |field, info|
case info[:header]
when ‘eligible?’
“Y”
else
field
end
end
end
end

CSV.foreach(‘test.csv’) do |row|
p row
end

>> [“name”, “age”, “eligible?”]

>> [“arup”, “27”, " "]

>> [“deep”, “14”, " "]

>> [“Debu”, “26”, “y”]

>> [“Ram”, “30”, " "]

>> [“Sagar”, “14”, " "]

But my expected output is :

>> [“name”, “age”, “eligible?”]

>> [“arup”, “27”,“y”]

>> [“deep”, “14”, “y”]

>> [“Debu”, “26”, “y”]

>> [“Ram”, “30”, “y”]

>> [“Sagar”, “14”, “y”]

I have tested the below code is not getting executed :

csv_file.convert do |field, info|
case info[:header]
when ‘eligible?’
“Y”
else
field
end
end

Why so ?

Ammar A. wrote in post #1137014:

On Feb 18, 2014, at 10:39 AM, Arup R. [email protected] wrote:

Ruby is very advanced, but it does not read minds :wink:

Good luck,
Ammar

@Ammar. Thanks for continuous reply. Actually I am learning and trying
the Ruby’s CSV module here in my laptop. Some methods are very easy and
some are very tricky like this one.

For sometime, I am sending this post on hold. Let me try others, hope
there we will meet again… :slight_smile: :slight_smile:

Thanks again.

Ammar A. wrote in post #1136869:

On Feb 16, 2014, at 10:50 PM, Arup R. [email protected] wrote:

With a block you can accept one or two arguments. To convert based on
the header name, or index, you want to specify a block with two
arguments to get the field info:

Why CSV#convert not working here ?

require ‘csv’

str = CSV.generate(‘Hello’) do |csv|
csv.convert {|s| s.downcase }
end
str # => “Hello”

I expect the output as “hello”