2013-04-01 37 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