2017-01-04 8 views
0

私はオブジェクトの配列にいくつかの.csvファイルをロードするには、この機能を持っている:クラスの属性を取得し、それを一般的な方法で.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 

答えて

1

を私が見る最大の問題は、あなたの列ヘッダを正確にモデルの属性のように命名されていないこと、です。例: '日付'と '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.read」と「CSV.foreach」を置き換えるために、次のようになります。

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 
) 

エンド

'header.downcase'は 'ORDER_DATE'を 'order_date'に変換します。あなたは(私は本当に醜いことを考える)あなたのモデルに、属性モデルと異なる点は、各カラム名のセッターを追加する必要があります。

class Object < ActiveRecord::Base 
    alias_method :order_date=, :date= 

あなたはcsvファイルを取得することができればこれは必要ではないでしょうファイルでは、列ヘッダーの名前はモデル属性とまったく同じです。

私は、そのアイデアの1つがあなたに役立つことを願っています。

関連する問題