【问题标题】:Merge CSV files same unique ID with Ruby将 CSV 文件与 Ruby 合并相同的唯一 ID
【发布时间】:2019-10-09 12:59:05
【问题描述】:

我有两个 CSV 文件

file1.csv
username;userid;full_name;follower_count;following_count;media_count;email;category
helloworld;1234;data3;data4;data5;data6;data7;data8

file2.csv
username;owner_id;owner_profile_pic_url;media_url;tagged_brand_username
helloworld;1234;data3b;data4b;data5b

如果在file2.csv 中找不到file1.csv 用户名(例如第2 行),我需要使用带有空白的Ruby 输出以下文件。

output.csv
username;userid;full_name;follower_count;following_count;media_count;email;category;owner_profile_pic_url;media_url;tagged_brand_username
helloworld;1234;data3;data4;data5;data6;data7;data8;data3b;data4b;data5b
helloworld;1234;data3;data4;data5;data6;data7;data8;;;

目前我正在使用 Excel vlookup 函数。

谢谢

【问题讨论】:

  • 这个问题可以更清楚一些。您能否从files1.csvfile2.csv 中添加一两行以及预期的输出?我明白你想说什么,但 (uniqueID) 只会让它更令人困惑。
  • 完成,只是说我可以使用用户名或用户 ID 作为唯一 ID
  • 为什么output.csv 有两行。 output.csv 不应该有一个 helloworld;1234; 行吗?如果您试图演示不匹配的场景,请相应地更新第二行。
  • 请编辑您的问题,而不是通过评论回答要求澄清的问题,因为其他人可能会有相同的问题,并且不应期望阅读所有 cmets。换句话说,问题应该独立存在。此外,在使用评论回复评论时,请包含此人的用户名(例如,@JosephCho),以便 SO 通知他们已为他们留下评论;否则他们可能永远看不到您的评论。
  • 请澄清“如果在file2.csv 中找不到file1.csv 用户名,则为空白”是什么意思。你的意思是在那种情况下output.csv应该写成一个空文件?

标签: ruby csv


【解决方案1】:

在这个脚本中有很多东西需要解压。本质上,您需要将两个 CSV 文件读入哈希,将 file2 合并到 file1,然后将其写回 CSV。

require "csv"

dict = Hash.new
options = { col_sep: ";", headers: true}

# read file1
CSV.foreach("file1.csv", options) do |row|
  row = row.to_h
  user = "#{row['username']+row['userid']}"
  dict[user] = row
end

# read file2
CSV.foreach("file2.csv", options) do |row|
  row = row.to_h
  user = "#{row['username']+row['owner_id']}"
  row.delete('owner_id')
  dict[user] = row.merge(dict[user]) if dict[user]
end

# turn hash into rows
rows = [['username','userid','full_name','follower_count','following_count','media_count','email','category','owner_profile_pic_url','media_url','tagged_brand_username']]
dict.each do |key, value|
  row = rows[0].map{|h| value[h] || "" }
  rows.push(row)
end

# write to csv
File.write("output.csv", rows.map{|r| r.to_csv(col_sep: ";") }.join)

这涵盖了 file1 中存在匹配和不匹配用户名的情况。

# file1.csv
username;userid;full_name;follower_count;following_count;media_count;email;category
helloworld;1234;data3;data4;data5;data6;data7;data8
goodbyeworld;5678;data3;data4;data5;data6;data7;file2.csv

# file2.csv
username;owner_id;owner_profile_pic_url;media_url;tagged_brand_username
helloworld;1234;data3b;data4b;data5b

# output.csv
username;userid;full_name;follower_count;following_count;media_count;email;category;owner_profile_pic_url;media_url;tagged_brand_username
helloworld;1234;data3;data4;data5;data6;data7;data8;data3b;data4b;data5b
goodbyeworld;5678;data3;data4;data5;data6;data7;data8;"";"";""

如前所述,output.csv 中有两行具有相同 ID 的事实非常令人困惑。下次只需添加一个额外的行,显示如果没有匹配会发生什么。虽然这是一个很好的问题,但我们在 how to write an excellent question 上有指导方针。

【讨论】:

    【解决方案2】:

    现有两个 CSV 输入文件,我们希望创建一个 CSV 输出文件:

    FNAME1   = 'file1.csv'
    FNAME2   = 'file2.csv'    
    FILE_OUT = 'output.csv'
    

    让我们首先创建两个输入文件。

    File.write(FNAME1, "username;userid;full_name;follower_count;following_count;media_count;email;category\nhelloworld;1234;data3;data4;data5;data6;data7;data8\n")
      #=> 136
    

    File.write(FNAME2, "username;owner_id;owner_profile_pic_url;media_url;tagged_brand_username\nhelloworld;1234;data3b;data4b;data5b\n")
      #=> 109
    

    现在执行读取这些文件、操作其内容并写入输出文件的步骤。

    require 'csv'
    

    首先读取两个输入文件并将其内容保存在变量中。

    def read_csv(fname)
      CSV.read(fname, col_sep: ';', headers: true)
    end
    
    csv1 = read_csv(FNAME1)
      #=> #<CSV::Table mode:col_or_row row_count:2> 
    csv2 = read_csv(FNAME2)
      #=> #<CSV::Table mode:col_or_row row_count:2>
    

    注意:

    csv1.to_a
      #=> [["username",    "userid", "full_name", "follower_count", "following_count",
      #     "media_count", "email",  "category"],
      #    ["helloworld",  "1234",   "data3",     "data4",          "data5",
      #     "data6",       "data7",  "data8"]] 
    csv2.to_a
      #=>  [["username",   "owner_id", "owner_profile_pic_url", "media_url", "tagged_brand_username"],
      #     ["helloworld", "1234",     "data3b",                "data4b",    "data5b"]] 
    

    如您所见,这些是普通数组,所以如果我们希望此时可以忘记它们来自 CSV 文件并使用标准 Ruby 方法来创建所需的输出文件。

    现在查看"username" 的值在两个文件中是否相同:

    username1 = csv1['username'].first
      #=> "helloworld"
    username2 = csv2['username'].first
      #=> "helloworld"
    

    csv1['username']"helloworld" 列中创建一个包含所有值的数组。这里就是["helloworld"];因此.first。当然,csv2 也是如此。

    如果username1 == username2 #=&gt; false 我们执行了我不清楚的操作,则退出。从今以后,我假设这两个用户名是相等的。

    将两个文件的标题读入数组。

    headers1 = csv1.headers
      #=> ["username", "userid", "full_name", "follower_count", "following_count",
      #    "media_count", "email", "category"] 
    headers2 = csv2.headers
      #=> ["username", "owner_id", "owner_profile_pic_url", "media_url",
      #    "tagged_brand_username"] 
    

    输出文件将包含headers1中的所有列和headers2中的所有列,除了headers2中的"username""owner_id",所以接下来让我们去掉@中的那些标题987654342@:

    headers2 -= ["username", "owner_id"]
      #=> ["owner_profile_pic_url", "media_url", "tagged_brand_username"] 
    

    接下来检索第一个文件中标题的值:

    values1 = headers1.flat_map { |h| csv1[h] }
      #=> ["helloworld", "1234", "data3", "data4", "data5", "data6", "data7", "data8"]
    

    以及第二个文件中剩余标头的值:

    values2 = headers2.flat_map { |h| csv2[h] }
      #=> ["data3b", "data4b", "data5b"] 
    

    我们将修改下面的values2,所以我们需要保存它的当前大小:

    values2_size = values2.size
      #=> i
    

    输出文件中标题行之后的第一行是包含值:

    values1 += values2
      #=> ["helloworld", "1234", "data3", "data4", "data5", "data6", "data7", "data8",
      #    "data3b", "data4b", "data5b"] 
    

    第二行是包含:

    values2 = values1 - values2
      #=> ["helloworld", "1234", "data3", "data4", "data5", "data6", "data7", "data8",
    

    加上values2_size #=&gt; 3 空白字段。

    我们可以使用CSV 方法将其写入文件,但这样做与使用常规文件方法相比并没有任何优势。我们可以简单地将以下字符串写入文件。

    str = [(headers1 + headers2).join(';'),
           values1.join(';'),
           values2.join(';') + ';' * values2_size
          ].join("\n")
    

    puts str
    username;userid;full_name;follower_count;following_count;media_count;email;category;owner_profile_pic_url;media_url;tagged_brand_username
    helloworld;1234;data3;data4;data5;data6;data7;data8;data3b;data4b;data5b
    helloworld;1234;data3;data4;data5;data6;data7;data8;;;
    

    让我们开始吧。

    File.write(FILE_OUT, str)
      #=> 265
    

    请注意,如果 ab 是数组,则 a += ba -= b 分别扩展为 a = a + ba = a - b。我使用的 CSV 方法记录在 here

    我将把它留给 OP 将我讨论过的操作组合成一个方法。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-03-25
      • 1970-01-01
      • 2012-06-13
      • 1970-01-01
      • 1970-01-01
      • 2013-08-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多