2016-06-02 14 views
1

私はtsv(タブで区切られたデータ)ファイルをデータベースにインポートしようとしていますが、フォーマットされていません。 pricecountの列はスペースで区切られ(ヘッダー行を除く)、値は両方ともpriceキーに置かれ、すべてのデータが間違ったキー値のペアに移動します。RailsはCSVファイルのデータを正規化します

TSVファイル:/models/purchase.rb

purchaser name item description price count merchant address merchant name 
Alice Bob $10 off $20 of food 10.0 2 987 Fake St  Bob's Pizza 
Example Name $30 of awesome for $10 10.0 5 456 Unreal Rd Tom's Awesome Shop 
Name Three $20 Sneakers for $5 5.0 1  123 Fake St  Sneaker Store Emporium 
John Williams $20 Sneakers for $5 5.0 4  123 Fake St  Sneaker Store Emporium 

class Purchase < ActiveRecord::Base 
    # validates :item_price, :numericality => { :greater_than_or_equal_to => 0 } 

    def self.import(file) 
    CSV.foreach(file.path, :headers => true, 
         :header_converters => lambda { |h| h.downcase.gsub(' ', '_')}, 
         :col_sep => "\t" 
         ) do |row| 
         # debugger 
         purchase_hash = row.to_hash 
     Purchase.create!(purchase_hash) 
    end 
    end 
end 

私がモデルにデバッガでファイルとコメントをインポートしてrowを入力した場合、それが返されます。

#<CSV::Row "purchaser_name":"Alice Bob" "item_description":"$10 off $20 of food" "price":"10.0 2" "count":" 987 Fake St" "merchant_address":" Bob's Pizza" "merchant_name":nil>

row.inspectのリターン:あなたが見ることができるように、彼らはファイルにタブ区切りなかったため

"#<CSV::Row \"purchaser_name\":\"Alice Bob\" \"item_description\":\"$10 off $20 of food\" \"price\":\"10.0 2\" \"count\":\" 987 Fake St\" \"merchant_address\":\" Bob's Pizza\" \"merchant_name\":nil>"

price(10.0)とcount(2)は同じ値に踏み付けられています。

db/schema.rb

ActiveRecord::Schema.define(version: 20160601205154) do 

    create_table "purchases", force: :cascade do |t| 
    t.string "purchaser_name" 
    t.string "item_description" 
    t.string "price" 
    t.string "count" 
    t.string "merchant_address" 
    t.string "merchant_name" 
    t.datetime "created_at",  null: false 
    t.datetime "updated_at",  null: false 
    end 

end 

私はもともと整数として小数点データ型としてpricecountがあったが、解決策を見つけることを試みるために文字列にそれらをスイッチバック。

答えて

1

これに対する解決策は2倍です。最初の2つの部分にフィールドを分割しますコンバータを定義(およびプロセスの数字に変換)解析中:

CONVERTER_SPLIT_PRICE_COUNT = lambda do |value, info| 
    next value unless info.header == "price" 
    price, count = value.split 
    [ price.to_f, count.to_i ] 
end 

これは、アレイ、例えば内priceフィールドを回します"10.0 2"[10.0, 2]となる。

def row_to_hash_fixing_price_count(row) 
    row.headers.zip(row.fields.flatten).to_h 
end 

上記親配列に価格/カウント配列を平坦化(行の残りの部分:解析した後に、見当違いの値を修正し、正しいハッシュを返し、メソッドを定義

第二に、 )、その後、ヘッダー配列でそれを圧縮します。ヘッダーより多くのフィールドがあるので、末尾の余分な数字はnilです。

あなたは、このようにそれらを使用します:http://ideone.com/08wTPT

P.S.:

csv_opts = { 
    headers: true, 
    col_sep: "\t", 
    header_converters: ->(h) { h.downcase.tr(" ", "_") }, 
    converters: CONVERTER_SPLIT_PRICE_COUNT 
} 

data_out = CSV.new(data, csv_opts).map do |row| 
    row_to_hash_fixing_price_count(row) 
end 
# => [ { "purchaser_name" => "Alice Bob", 
#  "item_description" => "$10 off $20 of food", 
#  "price" => 10.0, 
#  "count" => 2, 
#  "merchant_address" => "987 Fake St", 
#  "merchant_name" => "Bob's Pizza" 
#  }, 
#  # ... 
# ] 

はあなたがここにアクションでそれを見ることができます一度に1つではなく、一括でレコードを作成することを検討してください。上記を考えると、create! accepts an array of hashes以降はPurchase.create!(data_out)とすることができます。

+0

本当にありがとうございました – alisontague

0

merchant_addressとmerchant_nameの値をシフトして、押しつぶされた価格とカウントされたfiledsを分割し、2つの値を価格設定してみるといいかもしれません(返すことができれば元に戻すことができます)カウント:

purchase_hash = row.to_hash 
purchase_hash[:merchant_name] = purchase_hash[:merchant_address] 
purchase_hash[:merchant_address] = purchase_hash[:count] 
splitted_price_count = purchase_hash[:price].split(" ") 
purchase_hash[:price] = splitted_price_count.first 
purchase_hash[:count] = splitted_price_count.last 
Purchase.create!(purchase_hash) 
関連する問題