2013-04-01 67 views
3

我有一個上傳CSV文件,我解析像這樣:紅寶石 - 從CSV插入條目到數據庫

CSV.foreach(@my_file.file.path) do |row| 
    puts row[1] 
end 

傳入的CSV文件至少有以下欄目:「ID」,「姓名」, 「號碼」,「電話」和「食物」。

我想這樣做:

CSV.foreach(@my_file.file.path) do |row| 
    //find the columns in "row" associated with "id", "name", "number" 
    //even though I don't know definitively which column they will be in 
    //for example, "name" may be the 2nd or 3rd or 4th column (etc) 

    //insert into my_table values(id, name, number) 

end 

注意,CSV文件將永遠有列名的第一行,但是從文件到文件,這些列的順序可能會有所不同。

+0

您需要CSV文件中的第一行來命名列,否則如何處理記錄? – Raffaele

+0

第一行確實有列名,但列的順序可能會因文件而異 – CodeGuy

回答

8

這裏的代碼片段只收集你所關心的領域爲散列的數組:

require 'csv' 

fields_to_insert = %w{ id name food number phone } 
rows_to_insert = [] 

CSV.foreach("stuff.csv", headers: true) do |row| 
    row_to_insert = row.to_hash.select { |k, v| fields_to_insert.include?(k) } 
    rows_to_insert << row_to_insert 
end 

鑑於stuff.csv以下內容:

junk1,name,junk2,food,id,junk4,number,phone 
foo,Jim,bar,pizza,123,baz,9,555-1212 
baz,Fred,bar,sushi,55,foo,44,555-1213 

rows_to_insert會包含:

[{"name"=>"Jim", 
    "food"=>"pizza", 
    "id"=>"123", 
    "number"=>"9", 
    "phone"=>"555-1212"}, 
{"name"=>"Fred", 
    "food"=>"sushi", 
    "id"=>"55", 
    "number"=>"44", 
    "phone"=>"555-1213"}] 

我會採取和使用activerecord-import要一次插入他們都:

SomeModel.import(rows_to_insert) 

你可以插入一個記錄在CSV循環時間,但這是低效的,因爲id通常是一個受保護的屬性,你不能大規模分配它,所以你必須這樣做插入一條記錄:

some_model = SomeModel.new(row_to_insert.select { |k, v| k != "id" } 
some_model.id = row_to_insert["id"] 
some_model.save! 

...或類似的東西。

+3

嘗試'row.to_hash.values_at(* fields_to_insert)'而不是'row.to_hash.select {| k,v | fields_to_insert.include?(k)}'。 –

3

如果第一行是標題名稱,則可以使用:headers => true選項至parse,以便將第一行用作數據的關鍵字。

text = File.read(@my_file.file.path) 
csv = CSV.parse(text, :headers => true) 
csv.each do |row| 
    row = row.to_hash.with_indifferent_access 
    YourModel.create!(row.to_hash.symbolize_keys) 
end 
+0

使用CSV.foreach代替File.read + CSV.parse – pguardiario