在Ruby中连接两个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

我有两个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") 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