首页 > 解决方案 > 将动态数据列附加到多个 CSV

问题描述

我有一个脚本可以下载多个名称相似的 CSV(即 data.csv、data(1).csv),我想在每个 CSV 中附加一列,然后将它们组合成一个 CSV。

例如,

data.csv 有 4 个标题(header_1、header_2 等),我想用变量 foobar 添加 header_5。对于 data.csv,foobar 是“鸭子”,因此对于 data.csv 拥有的每一行,header_5 都会有那么多鸭子。

data(1).csv 是一样的,但这次变量 foobar 现在是“狗”。并且该脚本将用许多狗填充 header_5。

在最后一步,这 2 个 CSV 将被合并——保留修改后的数据——成为一个巨大的 CSV。

我一直在考虑这个问题。我对 Ruby 了解不多,这种问题对我来说是新的,所以我希望我能很好地解释它。

我考虑过首先修改 CSV 以获得新的数据列,然后将它们组合起来,但我一直遇到 CSV 名称的问题。为了让我修改文件,我需要知道名称,所以我想到了通配符。但是,我将如何区分 CSV?它可能会覆盖数据。

我当前的代码有这个问题。

 def CSV_Creation (source)


        input_files = Dir.glob("data*.csv")

        all_headers = input_files.reduce([]) do |all_headers, file|
            header_line = File.open(file, &:gets)
            all_headers | CSV.parse_line(header_line)
        end


        CSV.open("out.csv", "a+") do |out|
            all_headers << "Source"
            out << all_headers 


            input_files.each do |file|
                CSV.foreach(file, headers: true) do |row|
                    out << all_headers.map { |header| row[header] }



                end
            end
        end
    end

我不完全确定如何使最后一列中的数据不被覆盖。

编辑

感谢您迄今为止的所有回复。我已经更新了希望更有意义的代码:

def CSV_Creation (source)



        l_source = {'lead_source' => "#{source}"}

        input_file = Dir.glob("data*.csv").last



        puts "Here is " + input_file


        rows = CSV.open(input_file, headers: true).map{ |row| row.to_h }


        rows.each { |h| h.merge!(l_source)}
        headers = rows.first.keys
       rows.first.keys.each {|k| puts k}



        csv_response = CSV.generate do |csv| 
            csv << headers
            rows.each do |row|
                csv << row.values_at(*headers) 
            end
        end
        File.open("#{source}.csv", "w") {|file| file.write(csv_response)}


    end 

这将创建两个不同的 csv 文件,其中包含适当的列和数据。现在我只需要弄清楚如何组合这两个文件。

第二次编辑

这就是最终代码的样子。它按照我的要求做,所以我认为没关系?

 def CSV_Creation (source)



        l_source = {'lead_source' => "#{source}"}

        input_file = Dir.glob("data*.csv").last



        puts "Here is " + input_file


        rows = CSV.open(input_file, headers: true).map{ |row| row.to_h }


        rows.each { |h| h.merge!(l_source)}
        headers = rows.first.keys
       rows.first.keys.each {|k| puts k}



        csv_response = CSV.generate do |csv| 
            csv << headers
            rows.each do |row|
                csv << row.values_at(*headers) 
            end
        end
        File.open("#{source}.csv", "w") {|file| file.write(csv_response)}


        input_files = Dir.glob("#{source}*.csv")


        all_headers = input_files.reduce([]) do |all_headers, file|
            header_line = File.open(file, &:gets)
            all_headers | CSV.parse_line(header_line)
        end

        CSV.open("out.csv", "a+") do |out|
            out << all_headers 

            input_files.each do |file|
                CSV.foreach(file, headers: true) do |row|
                    out << all_headers.map { |header| row[header] }
                end
            end
        end







    end 

非常感谢所有给我建议的人!!

标签: rubycsv

解决方案


我有一个愚蠢的方式来做你问的事情:

  • 在 out.csv 文件中加入每个 csv 文件的行(有一点安全性)
  • 告诉 source.csv 中哪个列来自文件
# idk what to do with source
def CSV_Creation (source)
    input_files = Dir.glob("data*.csv").map { |filename| File.open(filename) }

    headers = input_files.map(&:gets)
    # Fix for "empty" lines in data files
    line_fix = headers.map { |header| CSV.parse_line(header).map { ',' }.join }

    CSV.open("out.csv", "a+") do |out|
        # We add the header
        out.puts headers.map(&:chomp).join(',')
        # We try to read all the lines
        until (lines = input_files.map(&:gets)).concat.empty?
            out.puts lines.map.with_index do |line, index|
                line&.chomp || line_fix[index]
            end.join(',')
        end
    end

    # In order to know the names we'll store a csv associating header to the filename
    File.open('source.csv', 'w') do |f|
        f.puts headers.map(&:chomp).join(',')
        line = input_files.map.with_index do |file, index|
            ([file.path] * line_fix[index].size).to_csv
        end
        f.puts line.map(&:chomp).join(',')
    end
ensure
    input_files.each(&:close)
end

推荐阅读