読者です 読者をやめる 読者になる 読者になる

さわだのノート

書籍のお仕事に役立つかもしれない思いつきを記録しています。

RSS: 記事の更新情報 Rss Feed

CSVファイルの重複を削除する

CSVファイルやExcelのファイルを編集しているとき、特定の列の要素が重複している行を取り除きたくなることがあります。

例が微妙ですが、下記のような名前の一覧があったとします。

スティーブ,ジョブズ
ビル,ゲイツ
スティーブ,ウォズニアック
スティーブ,バルマー
ビル,クリントン
ジョージ,マイケル

ここから、ひとつの名字に対して一人だけが残るCSVファイルを作りたい。

スティーブ,バルマー
ビル,クリントン
ジョージ,マイケル

こういう状態になれば成功です。
例が微妙で今ひとつ使いどころがわかりにくいかもしれません……。

作業を始める前に、少しだけCSVファイルを手直ししておきます。

1,スティーブ,ジョブズ
2,ビル,ゲイツ
3,スティーブ,ウォズニアック
4,スティーブ,バルマー
5,ビル,クリントン
6,ジョージ,マイケル

コードはこんな感じです。

#!/usr/bin/env perl
use 5.12.3;
use warnings;
use utf8;
use open IO => qw/:utf8 :std/;

use Text::CSV;

my %hash;
my $csv = Text::CSV->new({ binary => 1 });

while (my $row = $csv->getline(*DATA)) {
  $hash{$row->[1]} = $row;
}

my @list = values %hash;
@list = sort {$a->[0] <=> $b->[0]} @list;

open my $io_out, ">:utf8", "newsamplecsv.csv";
for my $i (@list) {
  $csv->combine(@$i);
  print $io_out $csv->string, "\n";
  say $csv->string;
}

__DATA__
1,スティーブ,ジョブズ
2,ビル,ゲイツ
3,スティーブ,ウォズニアック
4,スティーブ,バルマー
5,ビル,クリントン
6,ジョージ,マイケル

簡単な説明を

Text::CSVモジュールでCSVファイルを解析して、

while (my $row = $csv->getline(*DATA)) {
  $hash{$row->[1]} = $row;
}

で「姓」列をハッシュのキーに、$rowをハッシュの値に代入しています。

ハッシュはキーが重複すると上書きされてしまいます。つまり、このハッシュの値を配列として取り出すと、「姓」の重複を取り除いたCSVファイルの行が作れるわけです。
この配列は順番がばらばらになっているので、sortで1列目の数字を基準に並び替えしておきます。

my @list = values %hash;
@list = sort {$a->[0] <=> $b->[0]} @list;

あとは各行の配列を$csvCSV形式にコンバートして、ファイルハンドルに書き込みます。
書き込みの結果はこんな感じになっているはずです。

4,"スティーブ","バルマー"
5,"ビル","クリントン"
6,"ジョージ","マイケル"

スクレイピングで適当に抽出したWebページのデータを、URLを元に重複を削除するといった感じで使っていたりします。