更改ruby哈希中的头名称

时间:2014-03-02 16:01:05

标签: ruby-on-rails ruby csv

我有一些代码可以将CSV文件导入到数据库中,但由于属性名称不匹配而在更改时遇到一些问题。

CSV文件中的标题如下所示:

Date,Visitors
02/01/2014,3483
01/01/2014,13636

我的数据库具有相同的字段,但它们在开头不使用大写字母:

date
visitors

当我导入文件时,它工作得很好但我必须手动编辑文件中的值并将标题更改为小写,以便它们在导入时匹配。这是我模型中的导入方法:

def self.import(file)
    CSV.foreach(file.path, headers: true) do |row|
      entry = find_by(date: Date.parse(row["Date"])) || new
      entry.update row.to_hash
      entry.save!
    end
end

我想知道是否有一种更简单的方法可以让我的方法在进入数据库之前更改哈希中的标题名称并失败,因此我可以删除一些自己编辑文件的手动干预。

我已经尝试过查看.slice方法,但它们看起来好像只是为了选择特定的属性,我发现一些纯粹的ruby使用.map来替换这些但是想知道rails是否包含更适合这个任务的东西?

感谢阅读!

2 个答案:

答案 0 :(得分:2)

您只需添加一个选项:header_converters => [:downcase]即可。添加此选项后,它会在标题字符串上调用String#downcase方法。

请查看以下代码,为您提供提示:

require 'csv'

str = <<_
Date,Visitors
02/01/2014,3483
01/01/2014,13636
_

File.write('a',str)

CSV.foreach('a',:headers => true,:header_converters => [:downcase]) do |row|
  p row.to_hash
end
# >> {"date"=>"02/01/2014", "visitors"=>"3483"}
# >> {"date"=>"01/01/2014", "visitors"=>"13636"}

所以,你的方法是:

def self.import(file)
    CSV.foreach( file.path, headers: true, :header_converters => [:downcase] ) do |row|
      entry = find_by(date: Date.parse(row["date"])) || new
      entry.update row.to_hash
      entry.save!
    end
end

答案 1 :(得分:1)

不需要任何花哨的东西,只需在调用#update之前操纵哈希:

def self.import(file)
  CSV.foreach(file.path, headers: true) do |row|
    entry = find_by(date: Date.parse(row["Date"])) || new

    attributes = row.to_hash
    attributes[:visitors] = attributes.delete("Visitors")
    attributes[:date]     = attributes.delete("Date")

    entry.update(attributes)
    entry.save!
  end
end

Hash#delete从哈希中删除条目并返回其值。我选择删除它以避免在哈希值上同时包含两个属性(即"Visitors""visitors")。

如果CSV上有更多属性,并且您不希望逐个列出它们,您可能希望采用稍微强大的解决方案:

CSV_MAP = {
  'Date'     => 'date',
  'Visitors' => 'visitors',
  # More attributes...
}

def update_with_csv_row(row)
  attributes = CSV_MAP.each_with_object({}) do |(csv_key, attribute_key), result|
    result[attribute_key] = row[csv_key]
  end

  update(attributes)
end

然后,只需在原始代码上调用#update_with_csv_row而不是#update