Another alternative could be to:
- convert to int if possible on every column
- declare explicitly the types expected for each column
- use the
Hash
to NamedTuple
conversion
This will work as long as in an expected text column there is no number. But it can show some handy tricks.
Unfortunately due to the type restrictions and some method the NamedTuple.from
method does not work directly on Hash
-like API as CSV row.
https://play.crystal-lang.org/#/r/74le
require "csv"
csv_data = %(name,min_value,max_value,mod_type
,,,
of Skill,5,7,i_atk_speed
of Ease,8,10,i_atk_speed
of Mastery,11,13,i_atk_speed
of Renown,14,17,i_atk_speed
of Clockwork,18,21,i_atk_speed)
csv = CSV.new csv_data, headers: true
alias TypedRow = {name: String, min_value: Int32, max_value: Int32, mod_type: String}
csv.each do |instance|
h = instance.row.to_h
next if h.values.all? &.empty?
h = h.transform_values { |v| v.to_i32? || v }
row = TypedRow.from(h)
pp! row # => {name: "of Skill", min_value: 5, max_value: 7, mod_type: "i_atk_speed"} : TypedRow
end
It would also be possible to create a NamedTuple.from_csv_row
that will apply conversion depending on the type of each component.
With that idea, the following can be done: https://play.crystal-lang.org/#/r/74lf
require "csv"
csv_data = %(name,min_value,max_value,mod_type
,,,
of Skill,5,7,i_atk_speed
of Ease,8,10,i_atk_speed
of Mastery,11,13,i_atk_speed
of Renown,14,17,i_atk_speed
of Clockwork,18,21,i_atk_speed)
csv = CSV.new csv_data, headers: true
alias TypedRow = {name: String, min_value: Int32, max_value: Int32, mod_type: String}
csv.each do |instance|
next if instance.row.to_a.all? &.empty?
row = TypedRow.from_csv_row(instance.row)
pp! row # => {name: "of Skill", min_value: 5, max_value: 7, mod_type: "i_atk_speed"} : TypedRow
end
struct NamedTuple
def self.from_csv_row(row : CSV::Row) : self
{% begin %}
NamedTuple.new(**{{T}}).from_csv_row(row)
{% end %}
end
def from_csv_row(row : CSV::Row)
{% begin %}
NamedTuple.new(
{% for key, value in T %}
{% if value == Int32.class %}
{{key.stringify}}: row["{{key}}"].to_i32,
{% else %}
{{key.stringify}}: row["{{key}}"],
{% end %}
{% end %}
)
{% end %}
end
end