在Rails中查找两个文件之间的差异

时间:2013-03-07 07:22:13

标签: ruby-on-rails ruby

我目前的剧本:

old_ids = []
File.open("missing.txt","w") do |result|
  File.open('query_result.csv', "r") do |old_copy| 
    old_copy.each_line do |line|
      old_ids << line.chomp.to_i #add exisiting ids to array
    end
  end

  File.open('item_info', "r") do |new_copy|
    new_copy.each_line do |line|
      if !old_ids.include? line.split("\t")[0].chomp.to_i #find all ids in new file that do not exist in old file and add them to missing.txt
         result.puts line.split("\t")[0].chomp.to_i
         puts "Adding #{line.split("\t")[0].chomp.to_i}"
          end
        end
      end
    end

如何重构这一点以提高效率。我解析的文件包含大约260万条记录,因此写入时需要很长时间才能执行。

3 个答案:

答案 0 :(得分:2)

最低的水果?将ArrayArray#include替换为HashHash#key?

old_ids = {}
File.open("missing.txt","w") do |result|
  File.foreach('query_result.csv') do |line|
    id = line.chomp.to_i
    old_ids[id] = id
  end

  File.foreach('item_info') do |line|
    id = line.split("\t")[0].chomp.to_i
    unless old_ids.key?(id)
      result.puts id
      puts "Adding #{id}"
    end
  end
end

原因很简单:Array#include?每次查找给定值时扫描整个数组,总体上给出O(n 2 )复杂度。另一方面,Hash#key?计算给定值的哈希值,然后执行查找以查看哈希表中是否存在给定键。摊销时间复杂度接近O(n)。

两者之间的简单测试用例(找到两个文件之间的公共行)产生:

$ time ruby include.rb
real    2m51.409s
user    2m51.246s
sys     0m0.138s

$ time ruby key.rb
real    0m0.092s
user    0m0.082s
sys     0m0.009s

这是两个文件,每个文件包含2个 16 行。在10,000行时,include仍然需要5秒,而key?需要29毫秒。

有200万行,key?不到4秒。我认为我不能等待Array#include?实施完成。

答案 1 :(得分:1)

当您想要检测其他ID时,您距离最佳解决方案还不远。您可以通过构建Set来加速查找,而不是将旧的ID放在数组中。它更快。

old_ids = Set.new
File.open("missing.txt","w") do |result|
  File.open('query_result.csv', "r") do |old_copy| 
    old_copy.each_line do |line|
      old_ids.add(line.chomp.to_i) #add exisiting ids to Set
    end
  end

  File.open('item_info', "r") do |new_copy|
    new_copy.each_line do |line|
      if !old_ids.include? line.split("\t")[0].chomp.to_i #test if the id exists in the Set old_ids
         result.puts line.split("\t")[0].chomp.to_i
         puts "Adding #{line.split("\t")[0].chomp.to_i}"
        end
      end
    end
  end
end

如果没有文件示例,我无法测试。您的代码中存在一个错误(old_wmt_ids)。

答案 2 :(得分:0)

看看Diffy,这是一个很酷的宝石,用于计算文件之间的差异。