1

e コマースによって取り込まれた 2 つの Csv ファイルを比較する必要があります。カタログは毎週変更されるため、新しいファイルのアイテム数が異なることを除いて、ファイルは常に類似しています。

CSV ファイルの例:

sku_code, description, price, url    
001, product one, 100, www.something.com/1 
002, prouct two, 150, www.something.com/2

別の日に抽出した 2 つのファイルを比較して、廃止された製品のリストと追加された製品のリストを作成したいと考えています。

私のインデックスは、カタログ内で一義的である Sku_code である必要があります。

私は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")) }

私の問題

1 つのケースを除いて、うまく機能します: の後のフィールドの 1 つsku_codeが変更された場合、製品は "新しい" (例: 価格の変更) と見なされますが、私のニーズでは同じ製品です。

sku_code行全体ではなく行のみを比較する最も賢い方法は何ですか?

4

3 に答える 3

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")。

2 つの架空の csv ファイルに対してテストしました。


old_products編集:誤って を使用して計算さnew_product_keysれましたが、これはタイプミスでした。私の回答を編集しようとした人に感謝します (残念ながら拒否されました)。

于 2013-07-25T14:31:28.567 に答える
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    

これで動くはずです。2 つの while ループと 1 つの if ステートメントが必要です。さらに情報が必要ですか? それともこれでいいですか?

csv ファイルで結果を表示したい場合は、csv gem を使用する方が簡単です。

于 2013-07-25T14:24:59.370 に答える
0

パフォーマンスに大きな懸念がないと仮定すると、コードの量を最小限に抑えたいと考えていると思います。パフォーマンスが問題になる場合でも、最も単純なアプローチから始めて、ニーズに基づいてそこから改良します。

コードを書かなければならないことが 1 つ減るため、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"
于 2013-07-25T15:03:22.990 に答える