Jes Jes - 1 month ago 5
Ruby Question

Ruby - reading from .csv and creating objects out of it

I have .csv file with rows of which every row represents one call with certain duration, number etc. I need to create array of Call objects - every Call.new expects Hash of parameters, so it's easy - it just takes rows from CSV. But for some reason it doesn't work - when I invoke

Call.new(raw_call)
it's
nil
.
It's also impossible for me to see any output - I placed
puts
in various places in code (inside blocks etc) and it simply doesn't show anything. I obviously have another class -
Call
, which holds initialize for Call etc.

require 'csv'
class CSVCallParser
attr_accessor :io

def initialize(io)
self.io = io
end

NAMES = {
a: :date,
b: :service,
c: :phone_number,
d: :duration,
e: :unit,
f: :cost
}

def run
parse do |raw_call|
parse_call(raw_call)
end
end

private

def parse_call(raw_call)
NAMES.each_with_object({}) do |name, title, memo|
memo[name] = raw_call[title.to_s]
end
end

def parse(&block)
CSV.parse(io, headers: true, header_converters: :symbol, &block)
end

end

CSVCallParser.new(ARGV[0]).run


Small sample of my .csv file: headers and one row:

"a","b","c","d","e","f"
"01.09.2016 08:49","International","48627843111","0:29","","0,00"

Answer

I noticed a few things that isn't going as expected. In the parse_call method,

def parse_call(raw_call) 
  NAMES.each_with_object({}) do |name, title, memo|
    memo[name] = raw_call[title.to_s]
  end
end

I tried to print name, title, and memo. I expected to get :a, :date, and {}, but what I actually got was [:a,:date],{}, and nil.

Also, raw_call headers are :a,:b,:c..., not :date, :service..., so you should be using raw_call[name], and converting that to string will not help, since the key is a symbol in the raw_call.

So I modified the function to

def parse_call(raw_call) 
  NAMES.each_with_object({}) do |name_title, memo|
    memo[name_title[1]] = raw_call[name_title[0]]
  end
end

name_title[1] returns the title (:date, :service, etc)

name_title[0] returns the name (:a, :b, etc)

Also, in this method

def run
  parse do |raw_call| 
    parse_call(raw_call)
  end
end

You are not returning any results you get, so you are getting nil,

So, I changed it to

  def run
    res = []
    parse do |raw_call| 
      res << parse_call(raw_call)
    end
    res 
  end

Now, if I output the line

p CSVCallParser.new(File.read("file1.csv")).run

I get (I added two more lines to the csv sample)

[{:date=>"01.09.2016 08:49", :service=>"International", :phone_number=>"48627843111", :duration=>"0:29", :unit=>"", :cost=>"0,00"},
 {:date=>"02.09.2016 08:49", :service=>"International", :phone_number=>"48622454111", :duration=>"1:29", :unit=>"", :cost=>"0,00"}, 
 {:date=>"03.09.2016 08:49", :service=>"Domestic", :phone_number=>"48627843111", :duration=>"0:29", :unit=>"", :cost=>"0,00"}]

If you want to run this program from the terminal like so

ruby csv_call_parser.rb calls.csv

(In this case, data.csv is passed in as an argument to ARGV)

You can do so by modifying the last line of the ruby file.

p CSVCallParser.new(File.read(ARGV[0])).run

This will also return the array with hashes like before.

Comments