【问题标题】:output to csv in a similar manner 'print' outputs to terminal?以类似的方式输出到csv“打印”输出到终端?
【发布时间】:2016-06-06 23:56:04
【问题描述】:

我的 ruby​​ 代码中包含的逻辑有点难以转换为 csv 输出,有没有办法以类似于 print 输出到终端时的工作方式附加每个 value

cop_log 是数千条记录的散列

cop_log = { 'A1' => 'val1', 'B1' => 'val2', 'C1' => 'val3', 'D1' => 'val4, 'E1' => 'val5', 'F1' => 'val6', 'G1' => 'val7', 'H1' => 'val8', 'I1' => 'val9', 'J1' => 'val10, 'K1' => 'val11', 'A2' => 'val12', 'B2' => 'val13', 'C2' => 'val14', 'D2' => 'val15, 'E2' => 'val16', 'F2' => 'val17', 'G2' => 'val18', 'H2' => 'val19', 'I2' => 'val20', 'J2' => 'val21, 'K2' => 'val22'}

cop_log.each do |key, value|
  if key.include?('K')
    print "#{value}\n"
  elsif key.include?('A')
    print "#{job_number},#{pm_lookup[job_number]},#{value},"
  elsif key.include?('B') || key.include?('C') || key.include?('D') ||
        key.include?('E') || key.include?('F') || key.include?('G') ||
        key.include?('H') || key.include?('I') || key.include?('J')
    print "#{value},"
  end
end

目前在终端中输出是这样的,我希望它以同样的方式打印到 csv:

val1, val2, val3, val4, val5, val6, val7, val8, val9, val10, val11
val12, val13, val14, val15, val16, val17, val18, val19, val10, val21, val22

阅读文档看起来常见的路线是执行以下操作,

CSV.open("path/to/file.csv", "wb") do |csv|
  csv << ["row", "of", "CSV", "data"]
  csv << ["another", "row"]
  # ...
end

不幸的是,我的代码结构会让这变得有点困难......

【问题讨论】:

  • 当我们学习 API 时,我们倾向于把自己画到角落里,并试图通过改变 API 而不是我们的代码来摆脱困境。最好顺其自然并按预期使用 API。你会发现你的代码更具可读性。如所写,您的代码不是惯用的,可以更清楚地重写。我认为这是一个重构的机会。至于你的“问题”,你一个都没问。我们可以猜到你需要帮助,但如果你能更清楚地说明你的目标,它会对我们和其他人有所帮助。
  • 你的keys 是什么样的?它们是您匹配的单个字母,还是您试图在字符串中查找字母?请阅读“How to Ask”和“minimal reproducible example”。我们需要最少的输入数据和预期的输出。
  • 感谢您的 cmets 和回答,密钥来自使用 creek gem 由 Excel 电子表格编译而成的哈希值。整个 excel 电子表格在我的散列中,散列键是每个单元格,如 A1、B1、C1、A2、B2、C2 等。我的代码正在整理每一行。它用于处理特定公司使用的特定 excel 文件模板。我会重新考虑我的方法。 @theTinMan
  • 此信息需要输入到您的问题中,就像您最初指定它一样。编辑问题,在您想要的地方添加信息,这样一切都有意义。不要使用“编辑”或“更新”标签,因为我们可以在使用 SO 的编辑历史记录时知道添加了什么。

标签: ruby csv


【解决方案1】:

这非常令人困惑,如果清理干净,将有助于使您的代码更具可读性和可维护性:

if key.include?('K')
  print "#{value}\n"
elsif key.include?('A')
  print "#{job_number},#{pm_lookup[job_number]},#{value},"
elsif key.include?('B') || key.include?('C') || key.include?('D') ||
  key.include?('E') || key.include?('F') || key.include?('G') ||
  key.include?('H') || key.include?('I') || key.include?('J')
  print "#{value},"
end

key 是什么?一串字符还是单个字符?如果您匹配单个字符,您可以执行以下操作:

if key == 'K'
  print "#{value}\n"
elsif key == 'A'
  print "#{job_number},#{pm_lookup[job_number]},#{value},"
elsif ('B'.. 'J').include?(key)
elsif 
  print "#{value},"
end

甚至转换:

elsif ('B'.. 'J').include?(key)

到:

elsif ('B'..'J') === key

如果您在字符串中搜索字符匹配:

if key['K']
  print "#{value}\n"
elsif key['A']
  print "#{job_number},#{pm_lookup[job_number]},#{value},"
elsif key[/[B-J]/]
  print "#{value},"
end

此时您已准备好清理 CSV 输出。我建议在类文档中使用 CSV 输出的the basic examples 之一重新开始。我还建议您仔细阅读“Comma-separated values”,以便您更熟悉 CSV 格式文件的期望。

(注意:此时输入样本哈希已添加到显示键的实际格式的问题中。)

我会让你弄清楚如何合并 CSV,但请考虑一下:

cop_log = { 'A1' => 'val1', 'B1' => 'val2', 'C1' => 'val3', 'D1' => 'val4', 'E1' => 'val5', 'F1' => 'val6', 'G1' => 'val7', 'H1' => 'val8', 'I1' => 'val9', 'J1' => 'val10', 'K1' => 'val11', 'A2' => 'val12', 'B2' => 'val13', 'C2' => 'val14', 'D2' => 'val15', 'E2' => 'val16', 'F2' => 'val17', 'G2' => 'val18', 'H2' => 'val19', 'I2' => 'val20', 'J2' => 'val21', 'K2' => 'val22'}
cop_log.values.each_slice(11) do |slice|
  puts slice.join(',')
end

# >> val1,val2,val3,val4,val5,val6,val7,val8,val9,val10,val11
# >> val12,val13,val14,val15,val16,val17,val18,val19,val20,val21,val22

您需要依赖 CSV 类来创建 CSV 文件;格式并不像看起来那么简单。


这是我如何编写代码的第一步:

require 'csv'

cop_log = { 'A1' => 'val1', 'B1' => 'val2', 'C1' => 'val3', 'D1' => 'val4', 'E1' => 'val5', 'F1' => 'val6', 'G1' => 'val7', 'H1' => 'val8', 'I1' => 'val9', 'J1' => 'val10', 'K1' => 'val11', 'A2' => 'val12', 'B2' => 'val13', 'C2' => 'val14', 'D2' => 'val15', 'E2' => 'val16', 'F2' => 'val17', 'G2' => 'val18', 'H2' => 'val19', 'I2' => 'val20', 'J2' => 'val21', 'K2' => 'val22'}

job_number = 1
pm_lookup = [0, 1]

CSV.open('./foo.csv', 'w') do |csv|
  cop_log.group_by{ |k, v| k[1] }.values.each do |row|
    csv << [ job_number, pm_lookup[job_number], *row.map{ |k, v| v } ]
  end
end

运行后,foo.csv 包含:

1,1,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10,val11
1,1,val12,val13,val14,val15,val16,val17,val18,val19,val20,val21,val22

我们不知道您的预期输出是什么,因为您没有告诉我们,但您可以使用该代码并找出答案。

以下是数据预处理的细分:

cop_log.group_by{ |k, v| k[1] } # => {"1"=>[["A1", "val1"], ["B1", "val2"], ["C1", "val3"], ["D1", "val4"], ["E1", "val5"], ["F1", "val6"], ["G1", "val7"], ["H1", "val8"], ["I1", "val9"], ["J1", "val10"], ["K1", "val11"]], "2"=>[["A2", "val12"], ["B2", "val13"], ["C2", "val14"], ["D2", "val15"], ["E2", "val16"], ["F2", "val17"], ["G2", "val18"], ["H2", "val19"], ["I2", "val20"], ["J2", "val21"], ["K2", "val22"]]}
       .values # => [[["A1", "val1"], ["B1", "val2"], ["C1", "val3"], ["D1", "val4"], ["E1", "val5"], ["F1", "val6"], ["G1", "val7"], ["H1", "val8"], ["I1", "val9"], ["J1", "val10"], ["K1", "val11"]], [["A2", "val12"], ["B2", "val13"], ["C2", "val14"], ["D2", "val15"], ["E2", "val16"], ["F2", "val17"], ["G2", "val18"], ["H2", "val19"], ["I2", "val20"], ["J2", "val21"], ["K2", "val22"]]]

在上面的代码中,我确保输入的顺序符合预期,在这种情况下,我按一位数行分组。更健壮的代码将使用\d+ 而不是1,并且还会排序以强制执行适当的顺序。

当您将数据从一种格式转换为另一种格式以供以后重复使用时,这一点非常重要。虽然当前的 Ruby 保证哈希将保持其插入顺序,但假设它不是一个好习惯,因为旧版本的 Ruby 没有这样做,并且您将代码移植到的其他语言可能无法保持顺序。相反,始终进行防御性编程,确保您将返回一致的结果。当你发现以前的尝试还不够时,你会在多大程度上做到这一点。这样编程很有趣。

最后,任何时候您的代码感觉笨拙或不流畅,是时候备份并查看您在做什么了。

【讨论】:

  • 不幸的是,每个键都是一个字母数字对,它直接来自 Excel 电子表格,A1、B1、C1、A2、B2、C2 等,不过谢谢您的回复
【解决方案2】:

你不能这样做吗?

CSV.open("path/to/file.csv", "wb") do |csv|
    cop_log.each do |key, value|
        if key.include?('A')
            csv << [ job_number, pm_lookup[job_number], value ]
        elsif key.include?('K') || key.include?('B') || key.include?('C') || key.include?('D') ||
            key.include?('E') || key.include?('F') || key.include?('G') ||
            key.include?('H') || key.include?('I') || key.include?('J')
            csv << [ value ]
      end
    end
end

【讨论】:

    【解决方案3】:
    def write_csv(cop_log, io = $stdout)
      cop_log.each do |key, value|
        if key.include?('K')
          io.print "#{value}\n"
        elsif key.include?('A')
          io.print "#{job_number},#{pm_lookup[job_number]},#{value},"
        elsif key.include?('B') || key.include?('C') || key.include?('D') ||
              key.include?('E') || key.include?('F') || key.include?('G') ||
              key.include?('H') || key.include?('I') || key.include?('J')
          io.print "#{value},"
        end
      end
    end
    

    然后

    File.open('path/to/file.csv', 'w') do |f|
      write_csv(cop_log, f)
    end
    

    【讨论】:

      【解决方案4】:

      参考代码

          CSV.open("test.csv", "ab") do |csv|
            b = {}
            check_num = 0
            cop_log.each do |key, value|
              num = key.gsub(/[^\d]/, '').to_i
              next if num < 8 ||
                      key.include?('L') || key.include?('M') || key.include?('N') ||
                      key.include?('O') || key.include?('P') || key.include?('Q') ||
                      key.include?('R') || key.include?('S') || key.include?('T') ||
                      key.include?('U') || key.include?('V')
              a = { key => value }
              b.merge!(a)
            end # end of each loop
            i = 8
            while ((b.length / 9) - 7) > i do
              csv << [ job_number, pm_lookup[job_number], b["A#{i}"], b["B#{i}"],
                      b["C#{i}"], b["D#{i}"], b["E#{i}"], b["F#{i}"], b["G#{i}"],
                      b["H#{i}"], b["I#{i}"], b["J#{i}"], b["K#{i}"] ]
              i += 1
            end
          end # end of CSV.open
      

      【讨论】:

        猜你喜欢
        • 2020-02-24
        • 1970-01-01
        • 2013-05-27
        • 1970-01-01
        • 1970-01-01
        • 2019-01-30
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多