How do I construct an array of different types given a comma-separated string and another array dictating the type?
By parsing CSV input taken from stdin, I have an array of column header Symbols:
cols = [:IndexSymbol, :PriceStatus, :UpdateExchange, :Last]
and a line of raw input:
raw = "$JX.T.CA,Open,T,933.36T 11:10:00.000"
I would like to construct an an array, cells from the raw input, where each element of cells is a type identified by the corresponding element in cols. What are the idiomatic Ruby-sh ways of doing this?
I have tried this, which works but doesn't really feel right.
1) First, define a class for each type which needs to be encapsulated:
class Sku
attr_accessor :mRoot, :mExch,, :mCountry
def initialize(root, exch, country)
@mRoot = root
@mExch = exch
@mCountry = country
end
end
class Price
attr_accessor :mPrice, :mExchange, :mTime
def initialize(price, exchange, time)
@mPrice = price
@mExchange = exchange
@mTime = time
end
end
2) Then, define conversion functions for each unique column type which needs to be converted:
def to_sku(raw)
raw.match('(\w+)\.(\w{0,1})\.(\w{,2})') { |m| Sku.new(m[1], m[2], m[3])}
end
def to_price(raw)
end
3) Create an array of strings from the input:
cells = raw.split(",")
4) And finally modify each element of cells in-place by constructing the type dictated by the corresponding column header:
cells.each_index do |i|
cells[i] = case cols[i]
when :IndexSymbol
to_sku(cells[i])
when :PriceStatus
cells[i].split(";").collect {|st| st.to_sym}
when :UpdateExchange
cells[i]
when :Last
cells[i].match('(\d*\.*\d*)(\w?) (\d{1,2}:\d{2}:\d{2}\.\d{3})') { |m| Price.new(m[1], m[2], m[3])}
else
puts "Unhandled column type (#{cols[i]}) from input string: \n#{cols}\n#{raw}"
exit -1
end
end
The parts that don't feel right are steps 3 and 4. How is this done in a more Ruby fashion? I was imagining some kind of super concise method like this, which exists only in my imagination:
cells = raw.split_using_convertor(",")
.split(","). One common format of CSV allows individual fields to be quoted with"on either side, and requires"characters to be escaped as"". Ruby’s CSV library can handle that:require 'csv', then useCSV.parseorCSV.parse_line.:snake_casefor symbols, and no use of hungarian notation. Also, if you are going to be creating a lot of PORO (Plain Old Ruby Object)s, then you could use a gem like virtus to get rid of the:attr_accessor/initializeboilerplate and make the attribute types explicit.