在Ruby中连接两个CSV文件而不使用表
我有两个CSV文件,其中的列有在Ruby中连接两个CSV文件而不使用表,ruby,csv,data-processing,Ruby,Csv,Data Processing,我有两个CSV文件,其中的列有A、B、C。&D、E、F。我想将这两个CSV文件合并到一个新文件中,其中包含File1.B=File2.E行,该行包含a、B/E、C、D、F。如何在不使用表的情况下实现此连接?如果您有如下CSV文件: first.csv: second.csv: 您可以这样做: require 'csv' first = CSV.read('first.csv') second = CSV.read('second.csv') CSV.open("result.csv", "w
A、B、C
。&<代码>D、E、F。我想将这两个CSV文件合并到一个新文件中,其中包含File1.B=File2.E
行,该行包含a、B/E、C、D、F
。如何在不使用表的情况下实现此连接?如果您有如下CSV文件:
first.csv:
second.csv:
您可以这样做:
require 'csv'
first = CSV.read('first.csv')
second = CSV.read('second.csv')
CSV.open("result.csv", "w") do |csv|
csv << %w[A B.E C D F]
first.each do |rowF|
second.each do |rowS|
csv << [rowF[0],rowF[1],rowF[2],rowS[0],rowS[2]] if rowF[1] == rowS[1]
end
end
end
Givens 我们得到以下信息 两个输入文件的路径:
fname1 = 't1.csv'
fname2 = 't2.csv'
target1 = 'B'
target2 = 'E'
输出文件的路径:
fname3 = 't3.csv'
两个输入文件中要匹配的标题名称:
fname1 = 't1.csv'
fname2 = 't2.csv'
target1 = 'B'
target2 = 'E'
我确实假设(与示例一样)这两个文件必然包含相同数量的行
创建测试文件
让我们首先创建两个文件:
str = [%w|A B C|, %w|1 1 1|, %w|2 2 2|, %w|3 4 5|, %w|6 9 9|].
map { |a| a.join(",") }.join("\n")
#=> "A,B,C\n1,1,1\n2,2,2\n3,4,5\n6,9,9"
File.write(fname1, str)
#=> 29
将输入文件读入CSV::Table
objects
读取fname1
时,我将使用:header\u converters
选项将header“B”
转换为“B/E”
。请注意,这并不需要知道标题为“B”的列的位置(或任何可能的位置)
构造要从每个输入文件写入的标题数组
headers1 = csv1.headers
#=> ["A", "B/E", "C"]
headers2 = csv2.headers - [target2]
#=> ["D", "F"]
创建输出文件
headers1 = csv1.headers
#=> ["A", "B/E", "C"]
headers2 = csv2.headers - [target2]
#=> ["D", "F"]
我们首先将新的头headers1+headers2
写入输出文件
接下来,对于满足条件的每一行索引i
(i=0
,对应于每个文件标题行之后的第一行),我们将csv1[i]
和csv2[i]
的元素作为单行写入headers1
和headers2
中具有标题的列中。在索引i
处写入行需要满足的条件是i
满足:
csv1[i][new_target1] == csv2[i][target2] #=> true
现在打开fname3
进行编写,先写标题,然后写正文
CSV.open(fname3, 'w') do |csv|
csv << headers1 + headers2
[csv1.size, csv2.size].min.times do |i|
csv << (headers1.map { |h| csv1[i][h] } +
headers2.map { |h| csv2[i][h] }) if
csv1[i][new_target1] == csv2[i][target2]
end
end
#=> 4
答案是使用GROUPBY创建一个哈希表,然后迭代哈希表的键。假设要加入的列在每个表中都是唯一的:
join_column = "whatever"
csv1 = CSV.table("file1.csv").group_by { |r| r[join_column] }
csv2 = CSV.table("file2.csv").group_by { |r| r[join_column] }
joined_data = csv1.keys.sort.map do |join_column_values|
csv1[join_column].first.merge(csv2[join_column].first)
end
如果列在每个表中不是唯一的,那么您需要决定如何处理这些情况,因为数组
csv1[join\u column]
和csv2[join\u column]
中不仅仅有第一个元素。您可以按照其他答案之一(即嵌套映射调用)中的建议进行O(mxn)连接,也可以根据某些条件对它们进行筛选或组合。选择取决于您的用例。发布一些示例数据和期望的结果查看您的问题历史,您似乎不接受问题的答案。不幸的是,这导致无法再得到答案。请参见示例:如果TL;博士,至少看看照片。祝你好运。我不确定我是否正确地解释了这个问题(尤其是对“加入”的引用)。如果第二个文件的最后两行互换,答案是否保持不变@回答“是”;我的回答是“不”。如果第二个文件的“9”
列中有多行“9”
,则这些行中的哪些行将与第一个文件中的当前行合并并写入文件?再次说“是”、“否”。现在,您在授予绿色复选标记方面的得分达到了10分之一。这就是我使用的,但我希望找到比O(mxn)更好的东西。如果您首先对数据进行排序并获得O(max(m log m,n log n)),您可以做得更好。如果使用由要加入的列键控的哈希表,还可以使用O(m+n)额外空间获得O(m+n)。
CSV.open(fname3, 'w') do |csv|
csv << headers1 + headers2
[csv1.size, csv2.size].min.times do |i|
csv << (headers1.map { |h| csv1[i][h] } +
headers2.map { |h| csv2[i][h] }) if
csv1[i][new_target1] == csv2[i][target2]
end
end
#=> 4
puts File.read(fname3)
A,B/E,C,D,F
1,1,1,21,41
6,9,9,26,239
join_column = "whatever"
csv1 = CSV.table("file1.csv").group_by { |r| r[join_column] }
csv2 = CSV.table("file2.csv").group_by { |r| r[join_column] }
joined_data = csv1.keys.sort.map do |join_column_values|
csv1[join_column].first.merge(csv2[join_column].first)
end