CSVファイルを読み込んで、キーが重複しているレコードだけを出力するスクリプトです。同じキーを持つデータが2件以上ある場合のみ出力するようにしています。
実際に応用する場合は、入出力データのファイル名などを変更してください。
なお、[3-2-2.キーの重複したデータだけを出力する]と異なり、入力データはキー順に並べ替えておく必要はありません。
# double_hash.rb # 内容 : 重複データ除去 # 前提 : 重複チェックしたいキーであらかじめソートしておく必要はない。 # Copyright (c) 2013-2015 Mitsuo Minagawa, All rights reserved. # (minagawa@fb3.so-net.ne.jp) # 使用方法 : c:\>ruby double_hash.rb # # 入力ファイル in1_file = open("double.txt","r") # 出力ファイル out1_file = open("double_only.txt","w") # 出力用ハッシュ out1 = Hash.new # 主処理 while (line1 = in1_file.gets) line1.chomp! #---区切り文字により、処理を変更する---------- #タブ区切りのとき # in1 = line1.split("\t",-1) #カンマ区切りのとき in1 = (line1 + ',') .scan(/"([^"\\]*(?:\\.[^"\\]*)*)",|([^,]*),/) .collect{|x,y| y || x.gsub(/(.)/, '\1')} #キー項目が単独のとき in1_key = in1[0] #キー項目が複数のとき #1番目と2番目と3番目の項目がキーとなる場合 # in1_key = in1[0] + in1[1] + in1[2] # ハッシュのキーがすでに存在する場合 if (out1.include?(in1_key)) double = out1[in1_key] double[0] += 1 double.push(line1) out1[in1_key] = double # ハッシュのキーが存在しない場合 else double = [] double[0] = 1 double.push(line1) out1[in1_key] = double end end # ハッシュのキーをソートして出力する out1.sort_by{|key| key }.each{|key,value| # 件数が2件以上のとき(重複データのとき) if (out1[key][0] > 1) out1[key].shift w_temp = [out1[key]].join("\n") out1_file.print "#{w_temp}\n" end } # ファイルのクローズ in1_file.close out1_file.close
それでは、スクリプトの解説を個別に行っていきましょう。
重複しているかどうかで振り分ける
# ハッシュのキーがすでに存在する場合 if (out1.include?(in1_key)) double = out1[in1_key] double[0] += 1 double.push(line1) out1[in1_key] = double # ハッシュのキーが存在しない場合 else double = [] double[0] = 1 double.push(line1) out1[in1_key] = double end
ハッシュのデータ部分を配列にしています。そして0番目の要素に同じキーが存在する件数を入れ、1番目以降の要素に実際のデータを入れていきます。ハッシュの要素に別の配列に代入することができます。
ハッシュのキーをソートして出力する
# ハッシュのキーをソートして出力する out1.sort_by{|key| key }.each{|key,value| # 件数が2件以上のとき(重複データのとき) if (out1[key][0] > 1) out1[key].shift w_temp = [out1[key]].join("\n") out1_file.print "#{w_temp}\n" end }
"out1[key][0]"とすることで、ハッシュのデータ部分の配列の0番目の要素を取り出すことができます。0番目の要素にキーごと件数をセットしていますので、2件以上の場合に出力しています。その際、0番目の要素は"shift"で削除してから出力しています。
その他注意すべきこと
その他、注意すべき点は入力データがタブ区切りなどの場合とキーが複数ある場合の処理です。
1.入力データがタブ区切りの場合
上記のスクリプトはCSV2形式以外の引用符(")を使用する場合を含め、あらゆるCSVファイルに対応できるようになっていますが、入力データがタブ区切りの場合は
#カンマ区切りのとき in1 = (line1 + ',') .scan(/"([^"\\]*(?:\\.[^"\\]*)*)",|([^,]*),/) .collect{|x,y| y || x.gsub(/(.)/, '\1')}
上記の部分を
in1 = line1.split("\t",-1);
と変更します。
2.キーが複数の項目から成り立っている場合
「in1_key = in1[0]」の部分を
in1_key = in1[0] + in1[1] + in1[2]
のように変更します(文字列の連結は「+」を使う)。
aaa,1 aaa,2 aaa,3 bbb,1 ccc,1 ccc,2 ddd,1 ddd,2 ddd,3 ddd,4 eee,1 fff,1 ggg,1 hhh,1 hhh,2 hhh,3
aaa,1 aaa,2 aaa,3 -------------------------------------------------------------------------------- ccc,1 ccc,2 -------------------------------------------------------------------------------- ddd,1 ddd,2 ddd,3 ddd,4 -------------------------------------------------------------------------------- hhh,1 hhh,2 hhh,3