原文:http://www.mattephraim.com/blog/2008/12/31/a-quick-little-extension-to-the-spreadsheet-gem/
由于公司业务需要上传大量EXCEL数据,以前使用ASP上传,控件会把EXCEL当做数据集来进行处理,现在换SPREADSHEET,只能当做数组,非常不方便,希望能够处理成HASH,搜了半天,貌似这篇是在说这个问题。小翻译一下,方便自己也方便别人。
-----------------------------------------
对Spreadsheet GEM的扩展
问题: 我工作中最无聊的事情之一就是把EXCEL传到数据库里面。通常,顾客会把一个需要导入数据库的电子表格给我。如果我足够幸运的话,需要导入的数据是已经按照格式整理好的了。在我使用RUBY Spreadsheet gem来导入并调整这些电子表格之前我尝试了很多种不同的方法来完成这项工作。然后我使用RUBY DBI库来把数据导入到数据库。
Spreadsheet在从一个EXCEL中读取数据方面非常出色,但是经常让我感到头痛的是我必须在读取之前知道字段的顺序。比如:如果EXCEL在第三列的值是"First Name",我就必须知道每行的第三列都存放的是名的值。我想如果我能够使用row[:first_name]来读取某行中的first name的值。我最近有点空闲时间,因此我决定看看有没有可能实现这个问题。
我的解决方案:我首先要做的事情就是在Spreadsheet库中创建我自己的row类。我决定我要创建一个叫做 HashRow的类,你可以通过使用以每列第一行的值作为符号来存取每行的值。简单的说,我假设第一行是标题行。[这句没看懂]因此,表头叫做First Name的会被翻译成符号 :first_name.
同时,我为HashRow增加了一些方便的方法,header?方法可以返回这行是否为标题行。empty?返回这行是否完全为空。
# Wraps Spreadsheet::Excel::Row row array with extra functionality class Spreadsheet::HashRow < Spreadsheet::Excel::Row attr_reader :index # Keeps the original row value array # and also creates a hash of values def initialize(row, col_hash, index) @val_array = row @val_hash = get_val_hash(col_hash) @index = index end # Is this row the first row in the spreadsheet? def header? @index === 0 end # Checks if every cell in the row is set to nil def empty? @val_array.compact.length === 0 end # Returns the value in the row based on the index # or key passed in. Integer values returns the row value # by index in the array and symbols return the value # for the symbol or string def [](value) if value.is_a? Integer @val_array[value] else @val_hash[value.to_s.downcase] end end private # Uses a hash columns to build another hash for the # values in the array with keys for the column heads def get_val_hash(col_hash) col_hash.keys.inject({}) do |acc, key| acc.merge(key => @val_array[col_hash[key]]) end end end
现在我已经完成了我的HashRow类,现在我需要打开Spreadsheet::Excel::Worksheet 类,重写row方法,实现使用一个新方法来返回HashRow类的一个实例。我把原来旧row方法和我写的新方法混写在一起。同时我也写了一个私有方法来判断每个字段的索引以及一个规范字段名称的私有方法。
# Extends Spreadsheet::Excel::Worksheet so that the Rows become HashRows class Spreadsheet::Excel::Worksheet # Override the original row method with a new method # that returns the custom HashRow class instead of an array alias_method :old_row, :row def row(value) Spreadsheet::HashRow.new(old_row(value), get_col_indexes, value) end private # Returns a hash that contains key/value pairs for the column # headers and the the index of each header def get_col_indexes @col_indexes ||= old_row(0).inject({}) do |hash, cell| hash.merge(get_col_key(cell.to_s) => hash.length) end end # Converts the name of a column header to a # specially formatted string def get_col_key(col) col.gsub(/[\(\)]+/, ""). gsub(/\s/, "_"). downcase end
一旦我把我新写的Spreadsheet扩展文件写好了,我就可以像以前那样使用Spreadsheet库,我可以通过行和索引来存取数据,也可以使用标题行。
Spreadsheet.open(FILE).worksheet(0).each do |row| unless row.empty? || row.header? puts row[:first_name] puts row[:last_name] end end