我有这个函数可以将几个.csv
文件加载到一个对象数组中:
def load_csv_data_to_order_objects
orders = []
get_data_paths("../my/path", ".csv").each do |path|
CSV.foreach(path, :headers => :first_row, :col_sep => ',', encoding: "ISO8859-1:utf-8") do |row|
orders.push Order.new(
:date => row["ORDER_DATE"],
:seller_id => row["SELLER_ID"],
:order_number => row["ORDER_NUMBER"],
:product_id => row["PRODUCT_ID"],
:quantity => row["QUANTITY"].to_i,
:sales_price => row["SALES_PRICE"].to_f,
)
end
end
orders
end
这样可行,但我需要将具有不同列数的.csv
个文件加载到不同类型的对象中。一般"形状"函数的含义相同,但对象属性不同。
为了最大限度地减少代码重复,我该如何创建此函数的更通用版本?
我想象这样的事情:
def load_csv_data_to_objects(search_path, file_extension, class_name)
objects = []
get_data_paths(search_path, file_extension).each do |path|
CSV.foreach(path, :headers => :first_row, :col_sep => ',', encoding: "ISO8859-1:utf-8") do |row|
objects.push class_name.new(
# How can I get a class's attributes and map them to .csv headers?
)
end
end
objects
end
答案 0 :(得分:1)
我看到的最大问题是,您的列标题的名称与您的模型属性不完全相同。示例:' date'与' ORDER_DATE'。很难猜测,属性日期应该填充列的内容' ORDER_DATE'。这使得任务变得更加复杂。
因此,我的第一个想法是在每个类上定义一个像
这样的映射class Order < ActiveRecord::Base
CSV_IMPORT_MAPPING = {
date: 'ORDER_DATE',
seller_id: 'SELLER_ID',
# ...
}
并使用此映射初始化循环中的对象:
CSV.foreach(path, :headers => :first_row, :col_sep => ',', encoding: "ISO8859-1:utf-8") do |row|
objects.push class_name.new(class_name::CVS_IMPORT_MAPPING.tap { |hash| hash.map { |class_attr, csv_attr| hash[class_attr] = row[csv_attr] } })
)
end
这基本上会从
转换CSV_IMPORT_MAPPING{ date: 'ORDER_DATE', seller_id: 'SELLER_ID' }
到列内容为值的哈希
{ date: '2016-12-01', seller_id: 12345 }
并将此哈希传递给新函数以实例化新对象。
不酷,但你得到一个很好的概述,csv列与哪个模型属性匹配,你获得了灵活性(例如,你可以决定不导入所有列,但只是哈希中的那些列。)
另一种方法可能是,取代“CSV.foreach”&#39;使用&#39; CSV.read&#39;要访问标题,遍历标题并使用它们来实例化对象:
csv = CSV.read(path, :headers => :first_row, :col_sep => ',', encoding: "ISO8859-1:utf-8") do |row|
instance = class_name.new
csv.headers.each do |header|
instance.send("#{header.downcase}=", row[header])
end
objects.push instance
)
端
&#39; header.downcase&#39;将转换&#39; ORDER_DATE&#39;到&#39; order_date&#39;,例如你需要在你的模型上为每个列名添加一个setter,它不同于你的模型属性(我认为它非常难看):
class Object < ActiveRecord::Base
alias_method :order_date=, :date=
如果您可以获得一个csv文件,则不需要这样做,其中列标题的名称与模型属性完全相同。
我希望,其中一个想法适合你。