【问题标题】:How to write data and headers to csv dynamically?如何将数据和标题动态写入csv?
【发布时间】:2014-08-05 12:02:32
【问题描述】:

我需要将标题和数据写入 csv。我面临的问题是我的数据可能会更改顺序。我想将我的数据完全映射到特定的标题列下。谁能告诉我怎么做?

例如:我的数据看起来像

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

如果零售商重复并且他的顺序不同,那么我无法在他的标题列下准确映射他的值(例如:品牌零售商名称)

如何创建一个行对象,其中键作为标题,值作为该标题的值,并将其推送到输出 csv 文件中?

【问题讨论】:

  • 你能不能更精确一点,例如向我们展示你卡在哪里的代码?
  • @leo 我已经编辑了我的问题。请看那个。
  • 您似乎在请人为您编写程序。由于情况并非如此,请说明您进行了哪些研究,以及您目前的知识状态中缺少什么。
  • @dcorking 我用我尝试过的逻辑编辑了我的问题。
  • 谢谢@user3843778。请解释您认为您的代码有什么问题。

标签: ruby csv fastercsv


【解决方案1】:

您的代码中似乎有几个错误。

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

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

  3. (第 4 行)也许您打算写 File.basename(path &lt;&lt; '.csv')

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

我认为 CSV 库没有检测前 2 列(例如第 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"]

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-03-21
    • 1970-01-01
    • 1970-01-01
    • 2018-05-15
    • 1970-01-01
    • 1970-01-01
    • 2017-12-03
    • 2022-01-09
    相关资源
    最近更新 更多