如何动态地将数据和标头写入csv?

时间:2014-08-05 12:02:32

标签: ruby csv fastercsv

我需要将标题和数据写入csv。我面临的问题是我的数据可能会改变顺序。我想在paricular标题列下准确映射我的数据。谁能告诉我怎么做?

Ex:我的数据看起来像是

Min6    ItemNumber  FC1 FC2 Retailer    Brand   Description  Size
630   12549        0     0             Too-Tart  SWEET&SOUR   .7OZ
                   0     0      Ahold  Too-Tart  SWEET&SOUR   .7OZ
                   0     0      Test   Too-Tart  SWEET&SOUR   .7OZ

630   12550        0     0             Too       SWEET&Salt   .60Z
                   0     0      Test   Too       SWEET&SOUR   .7OZ
                   0     0      Ahold  Too       SWEET&SOUR   .23Z

我期待

   Min6 ItemNumber  FC1 FC2 Retailer    Brand   Description     Size     Ahold-Description Ahold-Brand  Test-Description Test-Brand
   630    12549        0     0             Too-Tart  SWEET&SOUR .7OZ      SWEET&SOUR        Too-Tart     SWEET&SOUR       Too-Tart
   630    12549        0     0             Too-Tart  SWEET&Salt .6OZ      SWEET&SOUR        Too          SWEET&SOUR       Too

零售商的订单可能会发生变化,许多零售商都会在那里。对于每个新零售商,将创建新的标题。如果重复零售商,那么我想将他的品牌和描述映射到已经创建的标题下。

class CouponsDataPreProcessor < Transformer
  def run(path)
    @records = []
    input_base_name = File.basename(path, '.csv')
    first_row = true
    output_file = File.join('public', "coupons_data_preprocessor_#{input_base_name}_#{Time.now.strftime('%Y%m%d%H%M')}.csv")
    CSV.open(output_file, 'w') do |csv|
      CSV.foreach(path, {:headers => true}) do |row|
        if first_row
          @headers = row.headers
          first_row = false
        end
        if row['ItemNumber'].present?
          @records << row.fields
        else
          form_retailer_headers_and_fields(row)
        end
      end
      csv << @headers
      @records.each { |record| csv << record }
    end
    output_file
  end

  def form_retailer_headers_and_fields(row)
    retailer = row['Retailer']
    unless @headers.include?("Description-#{retailer}")
      @headers.push("Description-#{retailer}", "Brand-#{retailer}", "SubBrand-#{retailer}", "Size-#{retailer}")
    end
    @records.last.push(row['Description'], row['Brand'], row['SubBrand'], row['Size'])
  end

  def self.about
    'Preprocess coupons data into input line items'
  end
end

如果零售商重复并且他的顺序不同,那么我无法将他的价值完全映射到他的标题栏下(例如:品牌零售商名称)

如何使用key作为标头和值创建一个行对象作为该标头的值并将其推入输出csv文件?

1 个答案:

答案 0 :(得分:1)

您的代码中似乎存在多个错误。

  1. (第1行)您继承自Transformer,但您似乎无法重复使用Transformer中的任何方法。考虑删除继承。

  2. (第1行)您需要require 'csv'

  3. (第4行)也许你打算写File.basename(path << '.csv')

  4. (第14行)您编写了if row['ItemNumber'].present? 但如果该列为空,则行[&#39; ItemNumber&#39;]将为nil,因此程序将中止MethodMissing。 您可能打算在activesupport gem中使用present?方法,Stack Overflow用户需要测试这部分代码

  5. 我不认为CSV库有一个工具来检测前两列(例如第3行)是空白的,所以我认为你需要使用另一个工具来预处理,该工具在使用CSV之前标识列

    您可以编写一种方法来识别列号,但我只是笨拙且手动完成(请使用更整洁的代码编辑我的答案):

    COMMA = ','
    f = File.open(path)
    g = File.open('commas.csv', mode='w')
    lines = f.readlines
    lines = lines.reject{|ln| ln.length < 61} # reject the short line(s)
    lines_with_commas = lines.collect{|ln| ln[0..3] << COMMA << ln[4..17] \
    << COMMA << ln[18..23] << COMMA << ln[24..27] << COMMA << ln[28..38] \
    << COMMA << ln[39..47]<< COMMA << ln[48..60]<< COMMA << ln[61..-1]} 
    lines_with_commas.each{ |ln| g.write(ln)}
    g.close
    

    然后,您可以一次性导入CSV并搜索零售商。

    > table = CSV.table('commas.csv')
    > table[:retailer_].collect(&:strip).uniq
    => ["", "Ahold", "Test"]