Descriptions
Read file csv in ruby.
I have a csv file with this content
longitude,latitude,phone
13,139.7113134,35.56712836,0311112222
I read file csv.
Code
uploaded_io = params[:rooms][:file]
rooms_table = CSV.table(uploaded_io.tempfile, encoding: "UTF-8")
rooms_table.each_with_index do |row, i|
p row
end
puts row:
#<CSV::Row longitude:139.7113134 latitude:35.56712836 phone:52728978 >
I don't understand where is value phone number? I expect phone number is
0311112222 instead of 52728978
The reason this is happening is that, per the docs, CSV.table is:
A shortcut for:
CSV.read( path, { headers: true, converters: :numeric, header_converters: :symbol }.merge(options) )
Note converters: :numeric, which tells it to automatically (attempt to) convert numeric-looking fields to Numbers. Phone numbers, of course, aren't really numbers, but rather strings of digits.
If you don't want any converstions, you could pass converters: nil as an option to CSV.table.
Assuming you do want the :numeric converter to still operate on the other fields, though, you need to define your own converter. A converter is a Proc that takes two arguments: A field value and an (optional) FieldInfo object. Your converter might look like this:
NUMERIC_EXCEPT_PHONE_CONVERTER = lambda do |value, field_info|
if field_info.header == :phone
value
else
CSV::Converters[:float].call(
CSV::Converters[:integer].call(value))
end
end
Then you would use it by passing it to CSV.table as the converters: option, which will override the default converters: :numeric:
rooms_table = CSV.table("data.csv", encoding: "UTF-8", converters: NUMERIC_EXCEPT_PHONE_CONVERTER)
p rooms_table[0]
# => #<CSV::Row longitude:139.7113134 latitude:35.56712836 phone:"0311112222">
As you can see, the phone value is now a string with the leading 0.
You can see this code in action on repl.it: https://repl.it/@jrunning/WellmadeFarflungCron
Why, you might ask, is this bit so ugly?
CSV::Converters[:float].call(
CSV::Converters[:integer].call(value))
It's because the CSV module defines CSV::Converters thusly:
Converters = {
integer: lambda { |f|
Integer(f.encode(ConverterEncoding)) rescue f
},
float: lambda { |f|
Float(f.encode(ConverterEncoding)) rescue f
},
numeric: [:integer, :float],
# ...
}
Since the :numeric converter is not specified as a lambda, but rather an array that indicates that it's really just a "chain" of the :integer and :float converters, we can't just do CSV::Converters[:numeric].call(value); we have to call the two converters manually. (If anybody knows something I'm missing, please leave a comment.)
You can change:
rooms_table = CSV.table(uploaded_io.tempfile, encoding: "UTF-8")
to:
rooms_table = CSV.table(uploaded_io.tempfile, encoding: "UTF-8", converters: nil)
which will not convert/cast your fields (you will get strings). The default converter is :numeric which does this conversions that you don't want.
Possible converters that you can work with could be found here:
https://ruby-doc.org/stdlib-2.5.1/libdoc/csv/rdoc/CSV.html#Converters
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With