CSVファイルを読み込んで、入力データをそのまま出力するとともに、重複データに対して、最後に「重複」という文字をつけるスクリプトです。
キーごとにハッシュにデータをセットしておき、2件以上ある場合に「重複」という文字を付けて、出力しています。
実際に応用する場合は、入出力データのファイル名などを変更してください。
なお、[3-2-3.キーの重複したデータにマークをつけて、出力する]と異なり、入力データはキー順に並べ替えておく必要はありません。
# coding:windows-31j
# double2_hash.rb
# 内容 : 重複チェック後、重複データには、「重複」表示をする。
# 前提 : 重複チェックしたいキーであらかじめソートしておく必要はない。
# Copyright (c) 2013-2015 Mitsuo Minagawa, All rights reserved.
# (minagawa@fb3.so-net.ne.jp)
# 使用方法 : c:\>ruby double2_hash.rb
#
# 入力ファイル
in1_file = open("double.txt","r")
# 出力ファイル
out1_file = open("double_check.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"
else
out1[key].shift
out1[key].each {|data|
w_temp = [data,"重複"].join(",")
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"
else
out1[key].shift
out1[key].each {|data|
w_temp = [data,"重複"].join(",")
out1_file.print "#{w_temp}\n"
}
end
}
"out1[key][0]"とすることで、ハッシュのデータ部分の配列の0番目の要素を取り出すことができます。0番目の要素にキーごと件数をセットしていますので、1件だけの場合、そのまま出力し、2件以上ある場合、配列の要素を個別に出力しています。その際、0番目の要素は"shift"で削除してから出力します。
その他注意すべきこと
その他、注意すべき点は入力データがタブ区切りなどの場合とキーが複数ある場合の処理です。
1.入力データがタブ区切りの場合
上記のスクリプトはCSV2形式以外の引用符(")を使用する場合を含め、あらゆるCSVファイルに対応できるようになっていますが、入力データがタブ区切りの場合は
#カンマ区切りのとき
in1 = (line1 + ',')
.scan(/"([^"\\]*(?:\\.[^"\\]*)*)",|([^,]*),/)
.collect{|x,y| y || x.gsub(/(.)/, '\1')}
上記の部分を
in1 = line1.split("\t",-1);
と変更します。
と変更します。また、下記の箇所を
out1[key].each {|data|
temp = [data,"重複"].join(",")
out1_file.print "#{temp}\n"
}
以下のように変更します。
out1[key].each {|data|
temp = [data,"重複"].join("\t")
out1_file.print "#{temp}\n"
}
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,重複
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,重複