我必须比较两个由电子商务填充的Csv文件。这些文件总是相似的,只是较新的文件具有不同数量的项目,因为目录每周都会更改。
CSV文件示例:
sku_code, description, price, url
001, product one, 100, www.something.com/1
002, prouct two, 150, www.something.com/2
通过比较在不同日期提取的两个文件,我想生成一个已停产的产品清单和另一个已添加的产品清单。
我的索引应该是Sku_code,它在目录中是单义的。
我一直在使用this code from stackoverflow:
#old file
f1 = IO.readlines("oldfeed.csv").map(&:chomp)
#new file
f2 = IO.readlines("newfeed.csv").map(&:chomp)
#find new products
File.open("new_products.txt","w"){ |f| f.write((f2-f1).join("\n")) }
#find old products
File.open("deleted_products.txt","w"){ |f| f.write((f1-f2).join("\n")) }
我的问题
效果很好,除了一种情况:当sku_code
之后的一个字段发生变化时,产品被认为是“新的”(例如:价格的变化),即使对于我的需要,它也是相同的产品。
仅比较sku_code
而不是整行的最明智的方法是什么?
答案 0 :(得分:2)
无需使用CSV库,因为您对实际值不感兴趣(sku_code
除外)。我将每行放入一个以sku_code
为键的哈希值,比较sku_codes
,然后从这些哈希值中检索值。
#old file
f1 = IO.readlines("oldfeed.csv").map(&:chomp)
f1_hash = f1[1..-1].inject(Hash.new) {|hash,line| hash[line[/^\d+/]] = line; hash}
#new file
f2 = IO.readlines("newfeed.csv").map(&:chomp)
f2_hash = f2[1..-1].inject(Hash.new) {|hash,line| hash[line[/^\d+/]] = line; hash}
#find new products
new_product_keys = f2_hash.keys - f1_hash.keys
new_products = new_product_keys.map {|sku_code| f2_hash[sku_code] }
#find old products
old_product_keys = f1_hash.keys - f2_hash.keys
old_products = old_product_keys.map {|sku_code| f1_hash[sku_code] }
# write new products to file
File.open("new_products.txt","w") do |f|
f.write "#{f2.first}\n"
f.write new_products.join("\n")
end
#write old products to file
File.open("deleted_products.txt","w") do |f|
f.write "#{f1.first}\n"
f.write old_products.join("\n")
end
每个csv文件的第一行只包含列名。所以我跳过了每个csv文件的第一行(f1[1..-1]
),并在编写新文件(f.write "#{f1.first}\n"
)时添加了它。
测试了两个虚构的csv文件。
编辑:使用old_products
意外计算new_product_keys
,这是一个错字。感谢那些试图编辑我的答案的人(但不幸的是被拒绝了)。
答案 1 :(得分:0)
require 'csv'
#I'm really hungover
DOA = 'oldfeed.csv'
DOB = 'newfeed.csv'
#^this is where your files are located
DOC = 'finished_product.csv'
#this little guy here is a csv file that has the unique values
#you dont need to create this file, ruby will make it for you
holder_1 = CSV.read(DOA)
holder_2 = CSV.read(DOB)
#we just put both csv files into an array
#way too early to be up
#assuming the Sku_code is the first number '001'
#holder_1[0][0] = 001
#holder_1[1][0] = 002
这应该让你感动,你需要两个while循环和一个if语句,你需要更多的信息吗?或者你还好吗?
如果您希望csv文件向您显示结果,那么使用csv gem会更容易。
答案 2 :(得分:0)
假设您没有很大的性能问题,我认为您希望争取最少量的代码。即使性能是一个问题,我也会从最简单的方法开始,并根据您的需求进行改进。
我认为使用CSV gem是个不错的主意,因为编写代码要少一些。也就是说,这是另一种解决这个问题的方法。请注意,下面的diff
函数适用于数组或散列,并且与键的定义方式无关。它在内部使用一个数组进行键查找,但更改它以使用哈希是很简单的。
l1a = "001, product one, 100, www.something.com/1"
l2 = "002, prouct two, 150, www.something.com/2"
l1b = "001, product one, 120, www.something.com/1"
l3 = "003, product three, 100, www.something.com/1"
l4 = "004, product four, 100, www.something.com/1"
file_old = [l1a, l2, l3]
file_new = [l1b, l2, l4]
sku = -> (record) do
record.split(',')[0]
end
def diff(set1, set2, keyproc)
set2_keys = set2.collect {|e| keyproc.call(e)}
set1.reject {|e| set2_keys.include?(keyproc.call(e))}
end
puts diff(file_old, file_new, sku)
# => "003, product three, 100, www.something.com/1"
puts diff(file_new, file_old, sku)
# => "004, product four, 100, www.something.com/1"